xref: /onnv-gate/usr/src/cmd/mdb/common/modules/libumem/umem.c (revision 1528:30ad6286c4a5)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*1528Sjwadams  * Common Development and Distribution License (the "License").
6*1528Sjwadams  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*1528Sjwadams  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include "umem.h"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <sys/vmem_impl_user.h>
310Sstevel@tonic-gate #include <umem_impl.h>
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <alloca.h>
34*1528Sjwadams #include <limits.h>
350Sstevel@tonic-gate 
360Sstevel@tonic-gate #include "misc.h"
37*1528Sjwadams #include "leaky.h"
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #include "umem_pagesize.h"
400Sstevel@tonic-gate 
410Sstevel@tonic-gate #define	UM_ALLOCATED		0x1
420Sstevel@tonic-gate #define	UM_FREE			0x2
430Sstevel@tonic-gate #define	UM_BUFCTL		0x4
440Sstevel@tonic-gate #define	UM_HASH			0x8
450Sstevel@tonic-gate 
46*1528Sjwadams int umem_ready;
47*1528Sjwadams 
48*1528Sjwadams static int umem_stack_depth_warned;
49*1528Sjwadams static uint32_t umem_max_ncpus;
500Sstevel@tonic-gate uint32_t umem_stack_depth;
51*1528Sjwadams 
520Sstevel@tonic-gate size_t umem_pagesize;
530Sstevel@tonic-gate 
540Sstevel@tonic-gate #define	UMEM_READVAR(var)				\
550Sstevel@tonic-gate 	(umem_readvar(&(var), #var) == -1 &&		\
56*1528Sjwadams 	    (mdb_warn("failed to read "#var), 1))
570Sstevel@tonic-gate 
580Sstevel@tonic-gate int
59*1528Sjwadams umem_update_variables(void)
600Sstevel@tonic-gate {
610Sstevel@tonic-gate 	size_t pagesize;
620Sstevel@tonic-gate 
630Sstevel@tonic-gate 	/*
64*1528Sjwadams 	 * Figure out which type of umem is being used; if it's not there
65*1528Sjwadams 	 * yet, succeed quietly.
660Sstevel@tonic-gate 	 */
67*1528Sjwadams 	if (umem_set_standalone() == -1) {
68*1528Sjwadams 		umem_ready = 0;
69*1528Sjwadams 		return (0);		/* umem not there yet */
70*1528Sjwadams 	}
71*1528Sjwadams 
72*1528Sjwadams 	/*
73*1528Sjwadams 	 * Solaris 9 used a different name for umem_max_ncpus.  It's
74*1528Sjwadams 	 * cheap backwards compatibility to check for both names.
75*1528Sjwadams 	 */
76*1528Sjwadams 	if (umem_readvar(&umem_max_ncpus, "umem_max_ncpus") == -1 &&
77*1528Sjwadams 	    umem_readvar(&umem_max_ncpus, "max_ncpus") == -1) {
78*1528Sjwadams 		mdb_warn("unable to read umem_max_ncpus or max_ncpus");
79*1528Sjwadams 		return (-1);
80*1528Sjwadams 	}
81*1528Sjwadams 	if (UMEM_READVAR(umem_ready))
820Sstevel@tonic-gate 		return (-1);
830Sstevel@tonic-gate 	if (UMEM_READVAR(umem_stack_depth))
840Sstevel@tonic-gate 		return (-1);
850Sstevel@tonic-gate 	if (UMEM_READVAR(pagesize))
860Sstevel@tonic-gate 		return (-1);
870Sstevel@tonic-gate 
880Sstevel@tonic-gate 	if (umem_stack_depth > UMEM_MAX_STACK_DEPTH) {
89*1528Sjwadams 		if (umem_stack_depth_warned == 0) {
90*1528Sjwadams 			mdb_warn("umem_stack_depth corrupted (%d > %d)\n",
91*1528Sjwadams 			    umem_stack_depth, UMEM_MAX_STACK_DEPTH);
92*1528Sjwadams 			umem_stack_depth_warned = 1;
93*1528Sjwadams 		}
940Sstevel@tonic-gate 		umem_stack_depth = 0;
950Sstevel@tonic-gate 	}
96*1528Sjwadams 
97*1528Sjwadams 	umem_pagesize = pagesize;
98*1528Sjwadams 
990Sstevel@tonic-gate 	return (0);
1000Sstevel@tonic-gate }
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate /*ARGSUSED*/
103*1528Sjwadams static int
1040Sstevel@tonic-gate umem_init_walkers(uintptr_t addr, const umem_cache_t *c, void *ignored)
1050Sstevel@tonic-gate {
1060Sstevel@tonic-gate 	mdb_walker_t w;
1070Sstevel@tonic-gate 	char descr[64];
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	(void) mdb_snprintf(descr, sizeof (descr),
1100Sstevel@tonic-gate 	    "walk the %s cache", c->cache_name);
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	w.walk_name = c->cache_name;
1130Sstevel@tonic-gate 	w.walk_descr = descr;
1140Sstevel@tonic-gate 	w.walk_init = umem_walk_init;
1150Sstevel@tonic-gate 	w.walk_step = umem_walk_step;
1160Sstevel@tonic-gate 	w.walk_fini = umem_walk_fini;
1170Sstevel@tonic-gate 	w.walk_init_arg = (void *)addr;
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate 	if (mdb_add_walker(&w) == -1)
1200Sstevel@tonic-gate 		mdb_warn("failed to add %s walker", c->cache_name);
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 	return (WALK_NEXT);
1230Sstevel@tonic-gate }
1240Sstevel@tonic-gate 
125*1528Sjwadams /*ARGSUSED*/
126*1528Sjwadams static void
127*1528Sjwadams umem_statechange_cb(void *arg)
128*1528Sjwadams {
129*1528Sjwadams 	static int been_ready = 0;
130*1528Sjwadams 
131*1528Sjwadams #ifndef _KMDB
132*1528Sjwadams 	leaky_cleanup(1);	/* state changes invalidate leaky state */
133*1528Sjwadams #endif
134*1528Sjwadams 
135*1528Sjwadams 	if (umem_update_variables() == -1)
136*1528Sjwadams 		return;
137*1528Sjwadams 
138*1528Sjwadams 	if (been_ready)
139*1528Sjwadams 		return;
140*1528Sjwadams 
141*1528Sjwadams 	if (umem_ready != UMEM_READY)
142*1528Sjwadams 		return;
143*1528Sjwadams 
144*1528Sjwadams 	been_ready = 1;
145*1528Sjwadams 	(void) mdb_walk("umem_cache", (mdb_walk_cb_t)umem_init_walkers, NULL);
146*1528Sjwadams }
147*1528Sjwadams 
148*1528Sjwadams int
149*1528Sjwadams umem_init(void)
150*1528Sjwadams {
151*1528Sjwadams 	mdb_walker_t w = {
152*1528Sjwadams 		"umem_cache", "walk list of umem caches", umem_cache_walk_init,
153*1528Sjwadams 		umem_cache_walk_step, umem_cache_walk_fini
154*1528Sjwadams 	};
155*1528Sjwadams 
156*1528Sjwadams 	if (mdb_add_walker(&w) == -1) {
157*1528Sjwadams 		mdb_warn("failed to add umem_cache walker");
158*1528Sjwadams 		return (-1);
159*1528Sjwadams 	}
160*1528Sjwadams 
161*1528Sjwadams 	if (umem_update_variables() == -1)
162*1528Sjwadams 		return (-1);
163*1528Sjwadams 
164*1528Sjwadams 	/* install a callback so that our variables are always up-to-date */
165*1528Sjwadams 	(void) mdb_callback_add(MDB_CALLBACK_STCHG, umem_statechange_cb, NULL);
166*1528Sjwadams 	umem_statechange_cb(NULL);
167*1528Sjwadams 
168*1528Sjwadams 	return (0);
169*1528Sjwadams }
170*1528Sjwadams 
1710Sstevel@tonic-gate int
1720Sstevel@tonic-gate umem_abort_messages(void)
1730Sstevel@tonic-gate {
1740Sstevel@tonic-gate 	char *umem_error_buffer;
1750Sstevel@tonic-gate 	uint_t umem_error_begin;
1760Sstevel@tonic-gate 	GElf_Sym sym;
1770Sstevel@tonic-gate 	size_t bufsize;
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	if (UMEM_READVAR(umem_error_begin))
1800Sstevel@tonic-gate 		return (DCMD_ERR);
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	if (umem_lookup_by_name("umem_error_buffer", &sym) == -1) {
1830Sstevel@tonic-gate 		mdb_warn("unable to look up umem_error_buffer");
1840Sstevel@tonic-gate 		return (DCMD_ERR);
1850Sstevel@tonic-gate 	}
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 	bufsize = (size_t)sym.st_size;
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	umem_error_buffer = mdb_alloc(bufsize+1, UM_SLEEP | UM_GC);
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	if (mdb_vread(umem_error_buffer, bufsize, (uintptr_t)sym.st_value)
1920Sstevel@tonic-gate 	    != bufsize) {
1930Sstevel@tonic-gate 		mdb_warn("unable to read umem_error_buffer");
1940Sstevel@tonic-gate 		return (DCMD_ERR);
1950Sstevel@tonic-gate 	}
1960Sstevel@tonic-gate 	/* put a zero after the end of the buffer to simplify printing */
1970Sstevel@tonic-gate 	umem_error_buffer[bufsize] = 0;
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	if ((umem_error_begin % bufsize) == 0)
2000Sstevel@tonic-gate 		mdb_printf("%s\n", umem_error_buffer);
2010Sstevel@tonic-gate 	else {
2020Sstevel@tonic-gate 		umem_error_buffer[(umem_error_begin % bufsize) - 1] = 0;
2030Sstevel@tonic-gate 		mdb_printf("%s%s\n",
2040Sstevel@tonic-gate 		    &umem_error_buffer[umem_error_begin % bufsize],
2050Sstevel@tonic-gate 		    umem_error_buffer);
2060Sstevel@tonic-gate 	}
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	return (DCMD_OK);
2090Sstevel@tonic-gate }
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate static void
2120Sstevel@tonic-gate umem_log_status(const char *name, umem_log_header_t *val)
2130Sstevel@tonic-gate {
2140Sstevel@tonic-gate 	umem_log_header_t my_lh;
2150Sstevel@tonic-gate 	uintptr_t pos = (uintptr_t)val;
2160Sstevel@tonic-gate 	size_t size;
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	if (pos == NULL)
2190Sstevel@tonic-gate 		return;
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 	if (mdb_vread(&my_lh, sizeof (umem_log_header_t), pos) == -1) {
2220Sstevel@tonic-gate 		mdb_warn("\nunable to read umem_%s_log pointer %p",
2230Sstevel@tonic-gate 		    name, pos);
2240Sstevel@tonic-gate 		return;
2250Sstevel@tonic-gate 	}
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 	size = my_lh.lh_chunksize * my_lh.lh_nchunks;
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	if (size % (1024 * 1024) == 0)
2300Sstevel@tonic-gate 		mdb_printf("%s=%dm ", name, size / (1024 * 1024));
2310Sstevel@tonic-gate 	else if (size % 1024 == 0)
2320Sstevel@tonic-gate 		mdb_printf("%s=%dk ", name, size / 1024);
2330Sstevel@tonic-gate 	else
2340Sstevel@tonic-gate 		mdb_printf("%s=%d ", name, size);
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate typedef struct umem_debug_flags {
2380Sstevel@tonic-gate 	const char	*udf_name;
2390Sstevel@tonic-gate 	uint_t		udf_flags;
2400Sstevel@tonic-gate 	uint_t		udf_clear;	/* if 0, uses udf_flags */
2410Sstevel@tonic-gate } umem_debug_flags_t;
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate umem_debug_flags_t umem_status_flags[] = {
2440Sstevel@tonic-gate 	{ "random",	UMF_RANDOMIZE,	UMF_RANDOM },
2450Sstevel@tonic-gate 	{ "default",	UMF_AUDIT | UMF_DEADBEEF | UMF_REDZONE | UMF_CONTENTS },
2460Sstevel@tonic-gate 	{ "audit",	UMF_AUDIT },
2470Sstevel@tonic-gate 	{ "guards",	UMF_DEADBEEF | UMF_REDZONE },
2480Sstevel@tonic-gate 	{ "nosignal",	UMF_CHECKSIGNAL },
2490Sstevel@tonic-gate 	{ "firewall",	UMF_FIREWALL },
2500Sstevel@tonic-gate 	{ "lite",	UMF_LITE },
2510Sstevel@tonic-gate 	{ NULL }
2520Sstevel@tonic-gate };
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate /*ARGSUSED*/
2550Sstevel@tonic-gate int
2560Sstevel@tonic-gate umem_status(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
2570Sstevel@tonic-gate {
2580Sstevel@tonic-gate 	int umem_logging;
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 	umem_log_header_t *umem_transaction_log;
2610Sstevel@tonic-gate 	umem_log_header_t *umem_content_log;
2620Sstevel@tonic-gate 	umem_log_header_t *umem_failure_log;
2630Sstevel@tonic-gate 	umem_log_header_t *umem_slab_log;
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	mdb_printf("Status:\t\t%s\n",
2660Sstevel@tonic-gate 	    umem_ready == UMEM_READY_INIT_FAILED ? "initialization failed" :
2670Sstevel@tonic-gate 	    umem_ready == UMEM_READY_STARTUP ? "uninitialized" :
2680Sstevel@tonic-gate 	    umem_ready == UMEM_READY_INITING ? "initialization in process" :
2690Sstevel@tonic-gate 	    umem_ready == UMEM_READY ? "ready and active" :
270*1528Sjwadams 	    umem_ready == 0 ? "not loaded into address space" :
2710Sstevel@tonic-gate 	    "unknown (umem_ready invalid)");
2720Sstevel@tonic-gate 
273*1528Sjwadams 	if (umem_ready == 0)
274*1528Sjwadams 		return (DCMD_OK);
275*1528Sjwadams 
2760Sstevel@tonic-gate 	mdb_printf("Concurrency:\t%d\n", umem_max_ncpus);
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 	if (UMEM_READVAR(umem_logging))
2790Sstevel@tonic-gate 		goto err;
2800Sstevel@tonic-gate 	if (UMEM_READVAR(umem_transaction_log))
2810Sstevel@tonic-gate 		goto err;
2820Sstevel@tonic-gate 	if (UMEM_READVAR(umem_content_log))
2830Sstevel@tonic-gate 		goto err;
2840Sstevel@tonic-gate 	if (UMEM_READVAR(umem_failure_log))
2850Sstevel@tonic-gate 		goto err;
2860Sstevel@tonic-gate 	if (UMEM_READVAR(umem_slab_log))
2870Sstevel@tonic-gate 		goto err;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	mdb_printf("Logs:\t\t");
2900Sstevel@tonic-gate 	umem_log_status("transaction", umem_transaction_log);
2910Sstevel@tonic-gate 	umem_log_status("content", umem_content_log);
2920Sstevel@tonic-gate 	umem_log_status("fail", umem_failure_log);
2930Sstevel@tonic-gate 	umem_log_status("slab", umem_slab_log);
2940Sstevel@tonic-gate 	if (!umem_logging)
2950Sstevel@tonic-gate 		mdb_printf("(inactive)");
2960Sstevel@tonic-gate 	mdb_printf("\n");
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 	mdb_printf("Message buffer:\n");
2990Sstevel@tonic-gate 	return (umem_abort_messages());
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate err:
3020Sstevel@tonic-gate 	mdb_printf("Message buffer:\n");
3030Sstevel@tonic-gate 	(void) umem_abort_messages();
3040Sstevel@tonic-gate 	return (DCMD_ERR);
3050Sstevel@tonic-gate }
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate typedef struct {
3080Sstevel@tonic-gate 	uintptr_t ucw_first;
3090Sstevel@tonic-gate 	uintptr_t ucw_current;
3100Sstevel@tonic-gate } umem_cache_walk_t;
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate int
3130Sstevel@tonic-gate umem_cache_walk_init(mdb_walk_state_t *wsp)
3140Sstevel@tonic-gate {
3150Sstevel@tonic-gate 	umem_cache_walk_t *ucw;
3160Sstevel@tonic-gate 	umem_cache_t c;
3170Sstevel@tonic-gate 	uintptr_t cp;
3180Sstevel@tonic-gate 	GElf_Sym sym;
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	if (umem_lookup_by_name("umem_null_cache", &sym) == -1) {
3210Sstevel@tonic-gate 		mdb_warn("couldn't find umem_null_cache");
3220Sstevel@tonic-gate 		return (WALK_ERR);
3230Sstevel@tonic-gate 	}
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	cp = (uintptr_t)sym.st_value;
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (umem_cache_t), cp) == -1) {
3280Sstevel@tonic-gate 		mdb_warn("couldn't read cache at %p", cp);
3290Sstevel@tonic-gate 		return (WALK_ERR);
3300Sstevel@tonic-gate 	}
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	ucw = mdb_alloc(sizeof (umem_cache_walk_t), UM_SLEEP);
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	ucw->ucw_first = cp;
3350Sstevel@tonic-gate 	ucw->ucw_current = (uintptr_t)c.cache_next;
3360Sstevel@tonic-gate 	wsp->walk_data = ucw;
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	return (WALK_NEXT);
3390Sstevel@tonic-gate }
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate int
3420Sstevel@tonic-gate umem_cache_walk_step(mdb_walk_state_t *wsp)
3430Sstevel@tonic-gate {
3440Sstevel@tonic-gate 	umem_cache_walk_t *ucw = wsp->walk_data;
3450Sstevel@tonic-gate 	umem_cache_t c;
3460Sstevel@tonic-gate 	int status;
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (umem_cache_t), ucw->ucw_current) == -1) {
3490Sstevel@tonic-gate 		mdb_warn("couldn't read cache at %p", ucw->ucw_current);
3500Sstevel@tonic-gate 		return (WALK_DONE);
3510Sstevel@tonic-gate 	}
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 	status = wsp->walk_callback(ucw->ucw_current, &c, wsp->walk_cbdata);
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 	if ((ucw->ucw_current = (uintptr_t)c.cache_next) == ucw->ucw_first)
3560Sstevel@tonic-gate 		return (WALK_DONE);
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	return (status);
3590Sstevel@tonic-gate }
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate void
3620Sstevel@tonic-gate umem_cache_walk_fini(mdb_walk_state_t *wsp)
3630Sstevel@tonic-gate {
3640Sstevel@tonic-gate 	umem_cache_walk_t *ucw = wsp->walk_data;
3650Sstevel@tonic-gate 	mdb_free(ucw, sizeof (umem_cache_walk_t));
3660Sstevel@tonic-gate }
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate typedef struct {
3690Sstevel@tonic-gate 	umem_cpu_t *ucw_cpus;
3700Sstevel@tonic-gate 	uint32_t ucw_current;
3710Sstevel@tonic-gate 	uint32_t ucw_max;
3720Sstevel@tonic-gate } umem_cpu_walk_state_t;
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate int
3750Sstevel@tonic-gate umem_cpu_walk_init(mdb_walk_state_t *wsp)
3760Sstevel@tonic-gate {
3770Sstevel@tonic-gate 	umem_cpu_t *umem_cpus;
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	umem_cpu_walk_state_t *ucw;
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 	if (umem_readvar(&umem_cpus, "umem_cpus") == -1) {
3820Sstevel@tonic-gate 		mdb_warn("failed to read 'umem_cpus'");
3830Sstevel@tonic-gate 		return (WALK_ERR);
3840Sstevel@tonic-gate 	}
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 	ucw = mdb_alloc(sizeof (*ucw), UM_SLEEP);
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	ucw->ucw_cpus = umem_cpus;
3890Sstevel@tonic-gate 	ucw->ucw_current = 0;
3900Sstevel@tonic-gate 	ucw->ucw_max = umem_max_ncpus;
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	wsp->walk_data = ucw;
3930Sstevel@tonic-gate 	return (WALK_NEXT);
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate int
3970Sstevel@tonic-gate umem_cpu_walk_step(mdb_walk_state_t *wsp)
3980Sstevel@tonic-gate {
3990Sstevel@tonic-gate 	umem_cpu_t cpu;
4000Sstevel@tonic-gate 	umem_cpu_walk_state_t *ucw = wsp->walk_data;
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 	uintptr_t caddr;
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 	if (ucw->ucw_current >= ucw->ucw_max)
4050Sstevel@tonic-gate 		return (WALK_DONE);
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	caddr = (uintptr_t)&(ucw->ucw_cpus[ucw->ucw_current]);
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 	if (mdb_vread(&cpu, sizeof (umem_cpu_t), caddr) == -1) {
4100Sstevel@tonic-gate 		mdb_warn("failed to read cpu %d", ucw->ucw_current);
4110Sstevel@tonic-gate 		return (WALK_ERR);
4120Sstevel@tonic-gate 	}
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	ucw->ucw_current++;
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	return (wsp->walk_callback(caddr, &cpu, wsp->walk_cbdata));
4170Sstevel@tonic-gate }
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate void
4200Sstevel@tonic-gate umem_cpu_walk_fini(mdb_walk_state_t *wsp)
4210Sstevel@tonic-gate {
4220Sstevel@tonic-gate 	umem_cpu_walk_state_t *ucw = wsp->walk_data;
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	mdb_free(ucw, sizeof (*ucw));
4250Sstevel@tonic-gate }
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate int
4280Sstevel@tonic-gate umem_cpu_cache_walk_init(mdb_walk_state_t *wsp)
4290Sstevel@tonic-gate {
4300Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
4310Sstevel@tonic-gate 		mdb_warn("umem_cpu_cache doesn't support global walks");
4320Sstevel@tonic-gate 		return (WALK_ERR);
4330Sstevel@tonic-gate 	}
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 	if (mdb_layered_walk("umem_cpu", wsp) == -1) {
4360Sstevel@tonic-gate 		mdb_warn("couldn't walk 'umem_cpu'");
4370Sstevel@tonic-gate 		return (WALK_ERR);
4380Sstevel@tonic-gate 	}
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate 	wsp->walk_data = (void *)wsp->walk_addr;
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	return (WALK_NEXT);
4430Sstevel@tonic-gate }
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate int
4460Sstevel@tonic-gate umem_cpu_cache_walk_step(mdb_walk_state_t *wsp)
4470Sstevel@tonic-gate {
4480Sstevel@tonic-gate 	uintptr_t caddr = (uintptr_t)wsp->walk_data;
4490Sstevel@tonic-gate 	const umem_cpu_t *cpu = wsp->walk_layer;
4500Sstevel@tonic-gate 	umem_cpu_cache_t cc;
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	caddr += cpu->cpu_cache_offset;
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 	if (mdb_vread(&cc, sizeof (umem_cpu_cache_t), caddr) == -1) {
4550Sstevel@tonic-gate 		mdb_warn("couldn't read umem_cpu_cache at %p", caddr);
4560Sstevel@tonic-gate 		return (WALK_ERR);
4570Sstevel@tonic-gate 	}
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	return (wsp->walk_callback(caddr, &cc, wsp->walk_cbdata));
4600Sstevel@tonic-gate }
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate int
4630Sstevel@tonic-gate umem_slab_walk_init(mdb_walk_state_t *wsp)
4640Sstevel@tonic-gate {
4650Sstevel@tonic-gate 	uintptr_t caddr = wsp->walk_addr;
4660Sstevel@tonic-gate 	umem_cache_t c;
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	if (caddr == NULL) {
4690Sstevel@tonic-gate 		mdb_warn("umem_slab doesn't support global walks\n");
4700Sstevel@tonic-gate 		return (WALK_ERR);
4710Sstevel@tonic-gate 	}
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), caddr) == -1) {
4740Sstevel@tonic-gate 		mdb_warn("couldn't read umem_cache at %p", caddr);
4750Sstevel@tonic-gate 		return (WALK_ERR);
4760Sstevel@tonic-gate 	}
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 	wsp->walk_data =
4790Sstevel@tonic-gate 	    (void *)(caddr + offsetof(umem_cache_t, cache_nullslab));
4800Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_next;
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	return (WALK_NEXT);
4830Sstevel@tonic-gate }
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate int
4860Sstevel@tonic-gate umem_slab_walk_partial_init(mdb_walk_state_t *wsp)
4870Sstevel@tonic-gate {
4880Sstevel@tonic-gate 	uintptr_t caddr = wsp->walk_addr;
4890Sstevel@tonic-gate 	umem_cache_t c;
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 	if (caddr == NULL) {
4920Sstevel@tonic-gate 		mdb_warn("umem_slab_partial doesn't support global walks\n");
4930Sstevel@tonic-gate 		return (WALK_ERR);
4940Sstevel@tonic-gate 	}
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), caddr) == -1) {
4970Sstevel@tonic-gate 		mdb_warn("couldn't read umem_cache at %p", caddr);
4980Sstevel@tonic-gate 		return (WALK_ERR);
4990Sstevel@tonic-gate 	}
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 	wsp->walk_data =
5020Sstevel@tonic-gate 	    (void *)(caddr + offsetof(umem_cache_t, cache_nullslab));
5030Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)c.cache_freelist;
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	/*
5060Sstevel@tonic-gate 	 * Some consumers (umem_walk_step(), in particular) require at
5070Sstevel@tonic-gate 	 * least one callback if there are any buffers in the cache.  So
5080Sstevel@tonic-gate 	 * if there are *no* partial slabs, report the last full slab, if
5090Sstevel@tonic-gate 	 * any.
5100Sstevel@tonic-gate 	 *
5110Sstevel@tonic-gate 	 * Yes, this is ugly, but it's cleaner than the other possibilities.
5120Sstevel@tonic-gate 	 */
5130Sstevel@tonic-gate 	if ((uintptr_t)wsp->walk_data == wsp->walk_addr)
5140Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_prev;
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	return (WALK_NEXT);
5170Sstevel@tonic-gate }
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate int
5200Sstevel@tonic-gate umem_slab_walk_step(mdb_walk_state_t *wsp)
5210Sstevel@tonic-gate {
5220Sstevel@tonic-gate 	umem_slab_t s;
5230Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
5240Sstevel@tonic-gate 	uintptr_t saddr = (uintptr_t)wsp->walk_data;
5250Sstevel@tonic-gate 	uintptr_t caddr = saddr - offsetof(umem_cache_t, cache_nullslab);
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	if (addr == saddr)
5280Sstevel@tonic-gate 		return (WALK_DONE);
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	if (mdb_vread(&s, sizeof (s), addr) == -1) {
5310Sstevel@tonic-gate 		mdb_warn("failed to read slab at %p", wsp->walk_addr);
5320Sstevel@tonic-gate 		return (WALK_ERR);
5330Sstevel@tonic-gate 	}
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 	if ((uintptr_t)s.slab_cache != caddr) {
5360Sstevel@tonic-gate 		mdb_warn("slab %p isn't in cache %p (in cache %p)\n",
5370Sstevel@tonic-gate 		    addr, caddr, s.slab_cache);
5380Sstevel@tonic-gate 		return (WALK_ERR);
5390Sstevel@tonic-gate 	}
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)s.slab_next;
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &s, wsp->walk_cbdata));
5440Sstevel@tonic-gate }
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate int
5470Sstevel@tonic-gate umem_cache(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
5480Sstevel@tonic-gate {
5490Sstevel@tonic-gate 	umem_cache_t c;
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
5520Sstevel@tonic-gate 		if (mdb_walk_dcmd("umem_cache", "umem_cache", ac, argv) == -1) {
5530Sstevel@tonic-gate 			mdb_warn("can't walk umem_cache");
5540Sstevel@tonic-gate 			return (DCMD_ERR);
5550Sstevel@tonic-gate 		}
5560Sstevel@tonic-gate 		return (DCMD_OK);
5570Sstevel@tonic-gate 	}
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags))
5600Sstevel@tonic-gate 		mdb_printf("%-?s %-25s %4s %8s %8s %8s\n", "ADDR", "NAME",
5610Sstevel@tonic-gate 		    "FLAG", "CFLAG", "BUFSIZE", "BUFTOTL");
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), addr) == -1) {
5640Sstevel@tonic-gate 		mdb_warn("couldn't read umem_cache at %p", addr);
5650Sstevel@tonic-gate 		return (DCMD_ERR);
5660Sstevel@tonic-gate 	}
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 	mdb_printf("%0?p %-25s %04x %08x %8ld %8lld\n", addr, c.cache_name,
5690Sstevel@tonic-gate 	    c.cache_flags, c.cache_cflags, c.cache_bufsize, c.cache_buftotal);
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	return (DCMD_OK);
5720Sstevel@tonic-gate }
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate static int
5750Sstevel@tonic-gate addrcmp(const void *lhs, const void *rhs)
5760Sstevel@tonic-gate {
5770Sstevel@tonic-gate 	uintptr_t p1 = *((uintptr_t *)lhs);
5780Sstevel@tonic-gate 	uintptr_t p2 = *((uintptr_t *)rhs);
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	if (p1 < p2)
5810Sstevel@tonic-gate 		return (-1);
5820Sstevel@tonic-gate 	if (p1 > p2)
5830Sstevel@tonic-gate 		return (1);
5840Sstevel@tonic-gate 	return (0);
5850Sstevel@tonic-gate }
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate static int
5880Sstevel@tonic-gate bufctlcmp(const umem_bufctl_audit_t **lhs, const umem_bufctl_audit_t **rhs)
5890Sstevel@tonic-gate {
5900Sstevel@tonic-gate 	const umem_bufctl_audit_t *bcp1 = *lhs;
5910Sstevel@tonic-gate 	const umem_bufctl_audit_t *bcp2 = *rhs;
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	if (bcp1->bc_timestamp > bcp2->bc_timestamp)
5940Sstevel@tonic-gate 		return (-1);
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 	if (bcp1->bc_timestamp < bcp2->bc_timestamp)
5970Sstevel@tonic-gate 		return (1);
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 	return (0);
6000Sstevel@tonic-gate }
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate typedef struct umem_hash_walk {
6030Sstevel@tonic-gate 	uintptr_t *umhw_table;
6040Sstevel@tonic-gate 	size_t umhw_nelems;
6050Sstevel@tonic-gate 	size_t umhw_pos;
6060Sstevel@tonic-gate 	umem_bufctl_t umhw_cur;
6070Sstevel@tonic-gate } umem_hash_walk_t;
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate int
6100Sstevel@tonic-gate umem_hash_walk_init(mdb_walk_state_t *wsp)
6110Sstevel@tonic-gate {
6120Sstevel@tonic-gate 	umem_hash_walk_t *umhw;
6130Sstevel@tonic-gate 	uintptr_t *hash;
6140Sstevel@tonic-gate 	umem_cache_t c;
6150Sstevel@tonic-gate 	uintptr_t haddr, addr = wsp->walk_addr;
6160Sstevel@tonic-gate 	size_t nelems;
6170Sstevel@tonic-gate 	size_t hsize;
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	if (addr == NULL) {
6200Sstevel@tonic-gate 		mdb_warn("umem_hash doesn't support global walks\n");
6210Sstevel@tonic-gate 		return (WALK_ERR);
6220Sstevel@tonic-gate 	}
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), addr) == -1) {
6250Sstevel@tonic-gate 		mdb_warn("couldn't read cache at addr %p", addr);
6260Sstevel@tonic-gate 		return (WALK_ERR);
6270Sstevel@tonic-gate 	}
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	if (!(c.cache_flags & UMF_HASH)) {
6300Sstevel@tonic-gate 		mdb_warn("cache %p doesn't have a hash table\n", addr);
6310Sstevel@tonic-gate 		return (WALK_DONE);		/* nothing to do */
6320Sstevel@tonic-gate 	}
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate 	umhw = mdb_zalloc(sizeof (umem_hash_walk_t), UM_SLEEP);
6350Sstevel@tonic-gate 	umhw->umhw_cur.bc_next = NULL;
6360Sstevel@tonic-gate 	umhw->umhw_pos = 0;
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	umhw->umhw_nelems = nelems = c.cache_hash_mask + 1;
6390Sstevel@tonic-gate 	hsize = nelems * sizeof (uintptr_t);
6400Sstevel@tonic-gate 	haddr = (uintptr_t)c.cache_hash_table;
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 	umhw->umhw_table = hash = mdb_alloc(hsize, UM_SLEEP);
6430Sstevel@tonic-gate 	if (mdb_vread(hash, hsize, haddr) == -1) {
6440Sstevel@tonic-gate 		mdb_warn("failed to read hash table at %p", haddr);
6450Sstevel@tonic-gate 		mdb_free(hash, hsize);
6460Sstevel@tonic-gate 		mdb_free(umhw, sizeof (umem_hash_walk_t));
6470Sstevel@tonic-gate 		return (WALK_ERR);
6480Sstevel@tonic-gate 	}
6490Sstevel@tonic-gate 
6500Sstevel@tonic-gate 	wsp->walk_data = umhw;
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 	return (WALK_NEXT);
6530Sstevel@tonic-gate }
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate int
6560Sstevel@tonic-gate umem_hash_walk_step(mdb_walk_state_t *wsp)
6570Sstevel@tonic-gate {
6580Sstevel@tonic-gate 	umem_hash_walk_t *umhw = wsp->walk_data;
6590Sstevel@tonic-gate 	uintptr_t addr = NULL;
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 	if ((addr = (uintptr_t)umhw->umhw_cur.bc_next) == NULL) {
6620Sstevel@tonic-gate 		while (umhw->umhw_pos < umhw->umhw_nelems) {
6630Sstevel@tonic-gate 			if ((addr = umhw->umhw_table[umhw->umhw_pos++]) != NULL)
6640Sstevel@tonic-gate 				break;
6650Sstevel@tonic-gate 		}
6660Sstevel@tonic-gate 	}
6670Sstevel@tonic-gate 	if (addr == NULL)
6680Sstevel@tonic-gate 		return (WALK_DONE);
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	if (mdb_vread(&umhw->umhw_cur, sizeof (umem_bufctl_t), addr) == -1) {
6710Sstevel@tonic-gate 		mdb_warn("couldn't read umem_bufctl_t at addr %p", addr);
6720Sstevel@tonic-gate 		return (WALK_ERR);
6730Sstevel@tonic-gate 	}
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &umhw->umhw_cur, wsp->walk_cbdata));
6760Sstevel@tonic-gate }
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate void
6790Sstevel@tonic-gate umem_hash_walk_fini(mdb_walk_state_t *wsp)
6800Sstevel@tonic-gate {
6810Sstevel@tonic-gate 	umem_hash_walk_t *umhw = wsp->walk_data;
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 	if (umhw == NULL)
6840Sstevel@tonic-gate 		return;
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	mdb_free(umhw->umhw_table, umhw->umhw_nelems * sizeof (uintptr_t));
6870Sstevel@tonic-gate 	mdb_free(umhw, sizeof (umem_hash_walk_t));
6880Sstevel@tonic-gate }
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate /*
6910Sstevel@tonic-gate  * Find the address of the bufctl structure for the address 'buf' in cache
6920Sstevel@tonic-gate  * 'cp', which is at address caddr, and place it in *out.
6930Sstevel@tonic-gate  */
6940Sstevel@tonic-gate static int
6950Sstevel@tonic-gate umem_hash_lookup(umem_cache_t *cp, uintptr_t caddr, void *buf, uintptr_t *out)
6960Sstevel@tonic-gate {
6970Sstevel@tonic-gate 	uintptr_t bucket = (uintptr_t)UMEM_HASH(cp, buf);
6980Sstevel@tonic-gate 	umem_bufctl_t *bcp;
6990Sstevel@tonic-gate 	umem_bufctl_t bc;
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 	if (mdb_vread(&bcp, sizeof (umem_bufctl_t *), bucket) == -1) {
7020Sstevel@tonic-gate 		mdb_warn("unable to read hash bucket for %p in cache %p",
7030Sstevel@tonic-gate 		    buf, caddr);
7040Sstevel@tonic-gate 		return (-1);
7050Sstevel@tonic-gate 	}
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	while (bcp != NULL) {
7080Sstevel@tonic-gate 		if (mdb_vread(&bc, sizeof (umem_bufctl_t),
7090Sstevel@tonic-gate 		    (uintptr_t)bcp) == -1) {
7100Sstevel@tonic-gate 			mdb_warn("unable to read bufctl at %p", bcp);
7110Sstevel@tonic-gate 			return (-1);
7120Sstevel@tonic-gate 		}
7130Sstevel@tonic-gate 		if (bc.bc_addr == buf) {
7140Sstevel@tonic-gate 			*out = (uintptr_t)bcp;
7150Sstevel@tonic-gate 			return (0);
7160Sstevel@tonic-gate 		}
7170Sstevel@tonic-gate 		bcp = bc.bc_next;
7180Sstevel@tonic-gate 	}
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 	mdb_warn("unable to find bufctl for %p in cache %p\n", buf, caddr);
7210Sstevel@tonic-gate 	return (-1);
7220Sstevel@tonic-gate }
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate int
7250Sstevel@tonic-gate umem_get_magsize(const umem_cache_t *cp)
7260Sstevel@tonic-gate {
7270Sstevel@tonic-gate 	uintptr_t addr = (uintptr_t)cp->cache_magtype;
7280Sstevel@tonic-gate 	GElf_Sym mt_sym;
7290Sstevel@tonic-gate 	umem_magtype_t mt;
7300Sstevel@tonic-gate 	int res;
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 	/*
7330Sstevel@tonic-gate 	 * if cpu 0 has a non-zero magsize, it must be correct.  caches
7340Sstevel@tonic-gate 	 * with UMF_NOMAGAZINE have disabled their magazine layers, so
7350Sstevel@tonic-gate 	 * it is okay to return 0 for them.
7360Sstevel@tonic-gate 	 */
7370Sstevel@tonic-gate 	if ((res = cp->cache_cpu[0].cc_magsize) != 0 ||
7380Sstevel@tonic-gate 	    (cp->cache_flags & UMF_NOMAGAZINE))
7390Sstevel@tonic-gate 		return (res);
7400Sstevel@tonic-gate 
741*1528Sjwadams 	if (umem_lookup_by_name("umem_magtype", &mt_sym) == -1) {
7420Sstevel@tonic-gate 		mdb_warn("unable to read 'umem_magtype'");
7430Sstevel@tonic-gate 	} else if (addr < mt_sym.st_value ||
7440Sstevel@tonic-gate 	    addr + sizeof (mt) - 1 > mt_sym.st_value + mt_sym.st_size - 1 ||
7450Sstevel@tonic-gate 	    ((addr - mt_sym.st_value) % sizeof (mt)) != 0) {
7460Sstevel@tonic-gate 		mdb_warn("cache '%s' has invalid magtype pointer (%p)\n",
7470Sstevel@tonic-gate 		    cp->cache_name, addr);
7480Sstevel@tonic-gate 		return (0);
7490Sstevel@tonic-gate 	}
7500Sstevel@tonic-gate 	if (mdb_vread(&mt, sizeof (mt), addr) == -1) {
7510Sstevel@tonic-gate 		mdb_warn("unable to read magtype at %a", addr);
7520Sstevel@tonic-gate 		return (0);
7530Sstevel@tonic-gate 	}
7540Sstevel@tonic-gate 	return (mt.mt_magsize);
7550Sstevel@tonic-gate }
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate /*ARGSUSED*/
7580Sstevel@tonic-gate static int
7590Sstevel@tonic-gate umem_estimate_slab(uintptr_t addr, const umem_slab_t *sp, size_t *est)
7600Sstevel@tonic-gate {
7610Sstevel@tonic-gate 	*est -= (sp->slab_chunks - sp->slab_refcnt);
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 	return (WALK_NEXT);
7640Sstevel@tonic-gate }
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate /*
7670Sstevel@tonic-gate  * Returns an upper bound on the number of allocated buffers in a given
7680Sstevel@tonic-gate  * cache.
7690Sstevel@tonic-gate  */
7700Sstevel@tonic-gate size_t
7710Sstevel@tonic-gate umem_estimate_allocated(uintptr_t addr, const umem_cache_t *cp)
7720Sstevel@tonic-gate {
7730Sstevel@tonic-gate 	int magsize;
7740Sstevel@tonic-gate 	size_t cache_est;
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	cache_est = cp->cache_buftotal;
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate 	(void) mdb_pwalk("umem_slab_partial",
7790Sstevel@tonic-gate 	    (mdb_walk_cb_t)umem_estimate_slab, &cache_est, addr);
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	if ((magsize = umem_get_magsize(cp)) != 0) {
7820Sstevel@tonic-gate 		size_t mag_est = cp->cache_full.ml_total * magsize;
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate 		if (cache_est >= mag_est) {
7850Sstevel@tonic-gate 			cache_est -= mag_est;
7860Sstevel@tonic-gate 		} else {
7870Sstevel@tonic-gate 			mdb_warn("cache %p's magazine layer holds more buffers "
7880Sstevel@tonic-gate 			    "than the slab layer.\n", addr);
7890Sstevel@tonic-gate 		}
7900Sstevel@tonic-gate 	}
7910Sstevel@tonic-gate 	return (cache_est);
7920Sstevel@tonic-gate }
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate #define	READMAG_ROUNDS(rounds) { \
7950Sstevel@tonic-gate 	if (mdb_vread(mp, magbsize, (uintptr_t)ump) == -1) { \
7960Sstevel@tonic-gate 		mdb_warn("couldn't read magazine at %p", ump); \
7970Sstevel@tonic-gate 		goto fail; \
7980Sstevel@tonic-gate 	} \
7990Sstevel@tonic-gate 	for (i = 0; i < rounds; i++) { \
8000Sstevel@tonic-gate 		maglist[magcnt++] = mp->mag_round[i]; \
8010Sstevel@tonic-gate 		if (magcnt == magmax) { \
8020Sstevel@tonic-gate 			mdb_warn("%d magazines exceeds fudge factor\n", \
8030Sstevel@tonic-gate 			    magcnt); \
8040Sstevel@tonic-gate 			goto fail; \
8050Sstevel@tonic-gate 		} \
8060Sstevel@tonic-gate 	} \
8070Sstevel@tonic-gate }
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate int
810*1528Sjwadams umem_read_magazines(umem_cache_t *cp, uintptr_t addr,
8110Sstevel@tonic-gate     void ***maglistp, size_t *magcntp, size_t *magmaxp, int alloc_flags)
8120Sstevel@tonic-gate {
8130Sstevel@tonic-gate 	umem_magazine_t *ump, *mp;
8140Sstevel@tonic-gate 	void **maglist = NULL;
8150Sstevel@tonic-gate 	int i, cpu;
8160Sstevel@tonic-gate 	size_t magsize, magmax, magbsize;
8170Sstevel@tonic-gate 	size_t magcnt = 0;
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 	/*
8200Sstevel@tonic-gate 	 * Read the magtype out of the cache, after verifying the pointer's
8210Sstevel@tonic-gate 	 * correctness.
8220Sstevel@tonic-gate 	 */
8230Sstevel@tonic-gate 	magsize = umem_get_magsize(cp);
824*1528Sjwadams 	if (magsize == 0) {
825*1528Sjwadams 		*maglistp = NULL;
826*1528Sjwadams 		*magcntp = 0;
827*1528Sjwadams 		*magmaxp = 0;
828*1528Sjwadams 		return (WALK_NEXT);
829*1528Sjwadams 	}
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 	/*
8320Sstevel@tonic-gate 	 * There are several places where we need to go buffer hunting:
8330Sstevel@tonic-gate 	 * the per-CPU loaded magazine, the per-CPU spare full magazine,
8340Sstevel@tonic-gate 	 * and the full magazine list in the depot.
8350Sstevel@tonic-gate 	 *
8360Sstevel@tonic-gate 	 * For an upper bound on the number of buffers in the magazine
8370Sstevel@tonic-gate 	 * layer, we have the number of magazines on the cache_full
8380Sstevel@tonic-gate 	 * list plus at most two magazines per CPU (the loaded and the
8390Sstevel@tonic-gate 	 * spare).  Toss in 100 magazines as a fudge factor in case this
8400Sstevel@tonic-gate 	 * is live (the number "100" comes from the same fudge factor in
8410Sstevel@tonic-gate 	 * crash(1M)).
8420Sstevel@tonic-gate 	 */
843*1528Sjwadams 	magmax = (cp->cache_full.ml_total + 2 * umem_max_ncpus + 100) * magsize;
8440Sstevel@tonic-gate 	magbsize = offsetof(umem_magazine_t, mag_round[magsize]);
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 	if (magbsize >= PAGESIZE / 2) {
8470Sstevel@tonic-gate 		mdb_warn("magazine size for cache %p unreasonable (%x)\n",
8480Sstevel@tonic-gate 		    addr, magbsize);
849*1528Sjwadams 		return (WALK_ERR);
8500Sstevel@tonic-gate 	}
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 	maglist = mdb_alloc(magmax * sizeof (void *), alloc_flags);
8530Sstevel@tonic-gate 	mp = mdb_alloc(magbsize, alloc_flags);
8540Sstevel@tonic-gate 	if (mp == NULL || maglist == NULL)
8550Sstevel@tonic-gate 		goto fail;
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	/*
8580Sstevel@tonic-gate 	 * First up: the magazines in the depot (i.e. on the cache_full list).
8590Sstevel@tonic-gate 	 */
8600Sstevel@tonic-gate 	for (ump = cp->cache_full.ml_list; ump != NULL; ) {
8610Sstevel@tonic-gate 		READMAG_ROUNDS(magsize);
8620Sstevel@tonic-gate 		ump = mp->mag_next;
8630Sstevel@tonic-gate 
8640Sstevel@tonic-gate 		if (ump == cp->cache_full.ml_list)
8650Sstevel@tonic-gate 			break; /* cache_full list loop detected */
8660Sstevel@tonic-gate 	}
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 	dprintf(("cache_full list done\n"));
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate 	/*
8710Sstevel@tonic-gate 	 * Now whip through the CPUs, snagging the loaded magazines
8720Sstevel@tonic-gate 	 * and full spares.
8730Sstevel@tonic-gate 	 */
874*1528Sjwadams 	for (cpu = 0; cpu < umem_max_ncpus; cpu++) {
8750Sstevel@tonic-gate 		umem_cpu_cache_t *ccp = &cp->cache_cpu[cpu];
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 		dprintf(("reading cpu cache %p\n",
8780Sstevel@tonic-gate 		    (uintptr_t)ccp - (uintptr_t)cp + addr));
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 		if (ccp->cc_rounds > 0 &&
8810Sstevel@tonic-gate 		    (ump = ccp->cc_loaded) != NULL) {
8820Sstevel@tonic-gate 			dprintf(("reading %d loaded rounds\n", ccp->cc_rounds));
8830Sstevel@tonic-gate 			READMAG_ROUNDS(ccp->cc_rounds);
8840Sstevel@tonic-gate 		}
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 		if (ccp->cc_prounds > 0 &&
8870Sstevel@tonic-gate 		    (ump = ccp->cc_ploaded) != NULL) {
8880Sstevel@tonic-gate 			dprintf(("reading %d previously loaded rounds\n",
8890Sstevel@tonic-gate 			    ccp->cc_prounds));
8900Sstevel@tonic-gate 			READMAG_ROUNDS(ccp->cc_prounds);
8910Sstevel@tonic-gate 		}
8920Sstevel@tonic-gate 	}
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	dprintf(("magazine layer: %d buffers\n", magcnt));
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate 	if (!(alloc_flags & UM_GC))
8970Sstevel@tonic-gate 		mdb_free(mp, magbsize);
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 	*maglistp = maglist;
9000Sstevel@tonic-gate 	*magcntp = magcnt;
9010Sstevel@tonic-gate 	*magmaxp = magmax;
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	return (WALK_NEXT);
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate fail:
9060Sstevel@tonic-gate 	if (!(alloc_flags & UM_GC)) {
9070Sstevel@tonic-gate 		if (mp)
9080Sstevel@tonic-gate 			mdb_free(mp, magbsize);
9090Sstevel@tonic-gate 		if (maglist)
9100Sstevel@tonic-gate 			mdb_free(maglist, magmax * sizeof (void *));
9110Sstevel@tonic-gate 	}
9120Sstevel@tonic-gate 	return (WALK_ERR);
9130Sstevel@tonic-gate }
9140Sstevel@tonic-gate 
9150Sstevel@tonic-gate static int
9160Sstevel@tonic-gate umem_walk_callback(mdb_walk_state_t *wsp, uintptr_t buf)
9170Sstevel@tonic-gate {
9180Sstevel@tonic-gate 	return (wsp->walk_callback(buf, NULL, wsp->walk_cbdata));
9190Sstevel@tonic-gate }
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate static int
9220Sstevel@tonic-gate bufctl_walk_callback(umem_cache_t *cp, mdb_walk_state_t *wsp, uintptr_t buf)
9230Sstevel@tonic-gate {
9240Sstevel@tonic-gate 	umem_bufctl_audit_t *b;
9250Sstevel@tonic-gate 	UMEM_LOCAL_BUFCTL_AUDIT(&b);
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 	/*
9280Sstevel@tonic-gate 	 * if UMF_AUDIT is not set, we know that we're looking at a
9290Sstevel@tonic-gate 	 * umem_bufctl_t.
9300Sstevel@tonic-gate 	 */
9310Sstevel@tonic-gate 	if (!(cp->cache_flags & UMF_AUDIT) ||
9320Sstevel@tonic-gate 	    mdb_vread(b, UMEM_BUFCTL_AUDIT_SIZE, buf) == -1) {
9330Sstevel@tonic-gate 		(void) memset(b, 0, UMEM_BUFCTL_AUDIT_SIZE);
9340Sstevel@tonic-gate 		if (mdb_vread(b, sizeof (umem_bufctl_t), buf) == -1) {
9350Sstevel@tonic-gate 			mdb_warn("unable to read bufctl at %p", buf);
9360Sstevel@tonic-gate 			return (WALK_ERR);
9370Sstevel@tonic-gate 		}
9380Sstevel@tonic-gate 	}
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 	return (wsp->walk_callback(buf, b, wsp->walk_cbdata));
9410Sstevel@tonic-gate }
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate typedef struct umem_walk {
9440Sstevel@tonic-gate 	int umw_type;
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate 	int umw_addr;			/* cache address */
9470Sstevel@tonic-gate 	umem_cache_t *umw_cp;
9480Sstevel@tonic-gate 	size_t umw_csize;
9490Sstevel@tonic-gate 
9500Sstevel@tonic-gate 	/*
9510Sstevel@tonic-gate 	 * magazine layer
9520Sstevel@tonic-gate 	 */
9530Sstevel@tonic-gate 	void **umw_maglist;
9540Sstevel@tonic-gate 	size_t umw_max;
9550Sstevel@tonic-gate 	size_t umw_count;
9560Sstevel@tonic-gate 	size_t umw_pos;
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 	/*
9590Sstevel@tonic-gate 	 * slab layer
9600Sstevel@tonic-gate 	 */
9610Sstevel@tonic-gate 	char *umw_valid;	/* to keep track of freed buffers */
9620Sstevel@tonic-gate 	char *umw_ubase;	/* buffer for slab data */
9630Sstevel@tonic-gate } umem_walk_t;
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate static int
9660Sstevel@tonic-gate umem_walk_init_common(mdb_walk_state_t *wsp, int type)
9670Sstevel@tonic-gate {
9680Sstevel@tonic-gate 	umem_walk_t *umw;
969*1528Sjwadams 	int csize;
9700Sstevel@tonic-gate 	umem_cache_t *cp;
971*1528Sjwadams 	size_t vm_quantum;
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate 	size_t magmax, magcnt;
9740Sstevel@tonic-gate 	void **maglist = NULL;
9750Sstevel@tonic-gate 	uint_t chunksize, slabsize;
9760Sstevel@tonic-gate 	int status = WALK_ERR;
9770Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
9780Sstevel@tonic-gate 	const char *layered;
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 	type &= ~UM_HASH;
9810Sstevel@tonic-gate 
9820Sstevel@tonic-gate 	if (addr == NULL) {
9830Sstevel@tonic-gate 		mdb_warn("umem walk doesn't support global walks\n");
9840Sstevel@tonic-gate 		return (WALK_ERR);
9850Sstevel@tonic-gate 	}
9860Sstevel@tonic-gate 
9870Sstevel@tonic-gate 	dprintf(("walking %p\n", addr));
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate 	/*
990*1528Sjwadams 	 * The number of "cpus" determines how large the cache is.
9910Sstevel@tonic-gate 	 */
992*1528Sjwadams 	csize = UMEM_CACHE_SIZE(umem_max_ncpus);
9930Sstevel@tonic-gate 	cp = mdb_alloc(csize, UM_SLEEP);
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	if (mdb_vread(cp, csize, addr) == -1) {
9960Sstevel@tonic-gate 		mdb_warn("couldn't read cache at addr %p", addr);
9970Sstevel@tonic-gate 		goto out2;
9980Sstevel@tonic-gate 	}
9990Sstevel@tonic-gate 
1000*1528Sjwadams 	/*
1001*1528Sjwadams 	 * It's easy for someone to hand us an invalid cache address.
1002*1528Sjwadams 	 * Unfortunately, it is hard for this walker to survive an
1003*1528Sjwadams 	 * invalid cache cleanly.  So we make sure that:
1004*1528Sjwadams 	 *
1005*1528Sjwadams 	 *	1. the vmem arena for the cache is readable,
1006*1528Sjwadams 	 *	2. the vmem arena's quantum is a power of 2,
1007*1528Sjwadams 	 *	3. our slabsize is a multiple of the quantum, and
1008*1528Sjwadams 	 *	4. our chunksize is >0 and less than our slabsize.
1009*1528Sjwadams 	 */
1010*1528Sjwadams 	if (mdb_vread(&vm_quantum, sizeof (vm_quantum),
1011*1528Sjwadams 	    (uintptr_t)&cp->cache_arena->vm_quantum) == -1 ||
1012*1528Sjwadams 	    vm_quantum == 0 ||
1013*1528Sjwadams 	    (vm_quantum & (vm_quantum - 1)) != 0 ||
1014*1528Sjwadams 	    cp->cache_slabsize < vm_quantum ||
1015*1528Sjwadams 	    P2PHASE(cp->cache_slabsize, vm_quantum) != 0 ||
1016*1528Sjwadams 	    cp->cache_chunksize == 0 ||
1017*1528Sjwadams 	    cp->cache_chunksize > cp->cache_slabsize) {
1018*1528Sjwadams 		mdb_warn("%p is not a valid umem_cache_t\n", addr);
1019*1528Sjwadams 		goto out2;
1020*1528Sjwadams 	}
1021*1528Sjwadams 
10220Sstevel@tonic-gate 	dprintf(("buf total is %d\n", cp->cache_buftotal));
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 	if (cp->cache_buftotal == 0) {
10250Sstevel@tonic-gate 		mdb_free(cp, csize);
10260Sstevel@tonic-gate 		return (WALK_DONE);
10270Sstevel@tonic-gate 	}
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate 	/*
10300Sstevel@tonic-gate 	 * If they ask for bufctls, but it's a small-slab cache,
10310Sstevel@tonic-gate 	 * there is nothing to report.
10320Sstevel@tonic-gate 	 */
10330Sstevel@tonic-gate 	if ((type & UM_BUFCTL) && !(cp->cache_flags & UMF_HASH)) {
10340Sstevel@tonic-gate 		dprintf(("bufctl requested, not UMF_HASH (flags: %p)\n",
10350Sstevel@tonic-gate 		    cp->cache_flags));
10360Sstevel@tonic-gate 		mdb_free(cp, csize);
10370Sstevel@tonic-gate 		return (WALK_DONE);
10380Sstevel@tonic-gate 	}
10390Sstevel@tonic-gate 
10400Sstevel@tonic-gate 	/*
10410Sstevel@tonic-gate 	 * Read in the contents of the magazine layer
10420Sstevel@tonic-gate 	 */
1043*1528Sjwadams 	if (umem_read_magazines(cp, addr, &maglist, &magcnt, &magmax,
1044*1528Sjwadams 	    UM_SLEEP) == WALK_ERR)
10450Sstevel@tonic-gate 		goto out2;
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate 	/*
10480Sstevel@tonic-gate 	 * We have all of the buffers from the magazines;  if we are walking
10490Sstevel@tonic-gate 	 * allocated buffers, sort them so we can bsearch them later.
10500Sstevel@tonic-gate 	 */
10510Sstevel@tonic-gate 	if (type & UM_ALLOCATED)
10520Sstevel@tonic-gate 		qsort(maglist, magcnt, sizeof (void *), addrcmp);
10530Sstevel@tonic-gate 
10540Sstevel@tonic-gate 	wsp->walk_data = umw = mdb_zalloc(sizeof (umem_walk_t), UM_SLEEP);
10550Sstevel@tonic-gate 
10560Sstevel@tonic-gate 	umw->umw_type = type;
10570Sstevel@tonic-gate 	umw->umw_addr = addr;
10580Sstevel@tonic-gate 	umw->umw_cp = cp;
10590Sstevel@tonic-gate 	umw->umw_csize = csize;
10600Sstevel@tonic-gate 	umw->umw_maglist = maglist;
10610Sstevel@tonic-gate 	umw->umw_max = magmax;
10620Sstevel@tonic-gate 	umw->umw_count = magcnt;
10630Sstevel@tonic-gate 	umw->umw_pos = 0;
10640Sstevel@tonic-gate 
10650Sstevel@tonic-gate 	/*
10660Sstevel@tonic-gate 	 * When walking allocated buffers in a UMF_HASH cache, we walk the
10670Sstevel@tonic-gate 	 * hash table instead of the slab layer.
10680Sstevel@tonic-gate 	 */
10690Sstevel@tonic-gate 	if ((cp->cache_flags & UMF_HASH) && (type & UM_ALLOCATED)) {
10700Sstevel@tonic-gate 		layered = "umem_hash";
10710Sstevel@tonic-gate 
10720Sstevel@tonic-gate 		umw->umw_type |= UM_HASH;
10730Sstevel@tonic-gate 	} else {
10740Sstevel@tonic-gate 		/*
10750Sstevel@tonic-gate 		 * If we are walking freed buffers, we only need the
10760Sstevel@tonic-gate 		 * magazine layer plus the partially allocated slabs.
10770Sstevel@tonic-gate 		 * To walk allocated buffers, we need all of the slabs.
10780Sstevel@tonic-gate 		 */
10790Sstevel@tonic-gate 		if (type & UM_ALLOCATED)
10800Sstevel@tonic-gate 			layered = "umem_slab";
10810Sstevel@tonic-gate 		else
10820Sstevel@tonic-gate 			layered = "umem_slab_partial";
10830Sstevel@tonic-gate 
10840Sstevel@tonic-gate 		/*
10850Sstevel@tonic-gate 		 * for small-slab caches, we read in the entire slab.  For
10860Sstevel@tonic-gate 		 * freed buffers, we can just walk the freelist.  For
10870Sstevel@tonic-gate 		 * allocated buffers, we use a 'valid' array to track
10880Sstevel@tonic-gate 		 * the freed buffers.
10890Sstevel@tonic-gate 		 */
10900Sstevel@tonic-gate 		if (!(cp->cache_flags & UMF_HASH)) {
10910Sstevel@tonic-gate 			chunksize = cp->cache_chunksize;
10920Sstevel@tonic-gate 			slabsize = cp->cache_slabsize;
10930Sstevel@tonic-gate 
10940Sstevel@tonic-gate 			umw->umw_ubase = mdb_alloc(slabsize +
10950Sstevel@tonic-gate 			    sizeof (umem_bufctl_t), UM_SLEEP);
10960Sstevel@tonic-gate 
10970Sstevel@tonic-gate 			if (type & UM_ALLOCATED)
10980Sstevel@tonic-gate 				umw->umw_valid =
10990Sstevel@tonic-gate 				    mdb_alloc(slabsize / chunksize, UM_SLEEP);
11000Sstevel@tonic-gate 		}
11010Sstevel@tonic-gate 	}
11020Sstevel@tonic-gate 
11030Sstevel@tonic-gate 	status = WALK_NEXT;
11040Sstevel@tonic-gate 
11050Sstevel@tonic-gate 	if (mdb_layered_walk(layered, wsp) == -1) {
11060Sstevel@tonic-gate 		mdb_warn("unable to start layered '%s' walk", layered);
11070Sstevel@tonic-gate 		status = WALK_ERR;
11080Sstevel@tonic-gate 	}
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate out1:
11110Sstevel@tonic-gate 	if (status == WALK_ERR) {
11120Sstevel@tonic-gate 		if (umw->umw_valid)
11130Sstevel@tonic-gate 			mdb_free(umw->umw_valid, slabsize / chunksize);
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 		if (umw->umw_ubase)
11160Sstevel@tonic-gate 			mdb_free(umw->umw_ubase, slabsize +
11170Sstevel@tonic-gate 			    sizeof (umem_bufctl_t));
11180Sstevel@tonic-gate 
1119*1528Sjwadams 		if (umw->umw_maglist)
1120*1528Sjwadams 			mdb_free(umw->umw_maglist, umw->umw_max *
1121*1528Sjwadams 			    sizeof (uintptr_t));
1122*1528Sjwadams 
11230Sstevel@tonic-gate 		mdb_free(umw, sizeof (umem_walk_t));
11240Sstevel@tonic-gate 		wsp->walk_data = NULL;
11250Sstevel@tonic-gate 	}
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate out2:
11280Sstevel@tonic-gate 	if (status == WALK_ERR)
11290Sstevel@tonic-gate 		mdb_free(cp, csize);
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 	return (status);
11320Sstevel@tonic-gate }
11330Sstevel@tonic-gate 
11340Sstevel@tonic-gate int
11350Sstevel@tonic-gate umem_walk_step(mdb_walk_state_t *wsp)
11360Sstevel@tonic-gate {
11370Sstevel@tonic-gate 	umem_walk_t *umw = wsp->walk_data;
11380Sstevel@tonic-gate 	int type = umw->umw_type;
11390Sstevel@tonic-gate 	umem_cache_t *cp = umw->umw_cp;
11400Sstevel@tonic-gate 
11410Sstevel@tonic-gate 	void **maglist = umw->umw_maglist;
11420Sstevel@tonic-gate 	int magcnt = umw->umw_count;
11430Sstevel@tonic-gate 
11440Sstevel@tonic-gate 	uintptr_t chunksize, slabsize;
11450Sstevel@tonic-gate 	uintptr_t addr;
11460Sstevel@tonic-gate 	const umem_slab_t *sp;
11470Sstevel@tonic-gate 	const umem_bufctl_t *bcp;
11480Sstevel@tonic-gate 	umem_bufctl_t bc;
11490Sstevel@tonic-gate 
11500Sstevel@tonic-gate 	int chunks;
11510Sstevel@tonic-gate 	char *kbase;
11520Sstevel@tonic-gate 	void *buf;
11530Sstevel@tonic-gate 	int i, ret;
11540Sstevel@tonic-gate 
11550Sstevel@tonic-gate 	char *valid, *ubase;
11560Sstevel@tonic-gate 
11570Sstevel@tonic-gate 	/*
11580Sstevel@tonic-gate 	 * first, handle the 'umem_hash' layered walk case
11590Sstevel@tonic-gate 	 */
11600Sstevel@tonic-gate 	if (type & UM_HASH) {
11610Sstevel@tonic-gate 		/*
11620Sstevel@tonic-gate 		 * We have a buffer which has been allocated out of the
11630Sstevel@tonic-gate 		 * global layer. We need to make sure that it's not
11640Sstevel@tonic-gate 		 * actually sitting in a magazine before we report it as
11650Sstevel@tonic-gate 		 * an allocated buffer.
11660Sstevel@tonic-gate 		 */
11670Sstevel@tonic-gate 		buf = ((const umem_bufctl_t *)wsp->walk_layer)->bc_addr;
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate 		if (magcnt > 0 &&
11700Sstevel@tonic-gate 		    bsearch(&buf, maglist, magcnt, sizeof (void *),
11710Sstevel@tonic-gate 		    addrcmp) != NULL)
11720Sstevel@tonic-gate 			return (WALK_NEXT);
11730Sstevel@tonic-gate 
11740Sstevel@tonic-gate 		if (type & UM_BUFCTL)
11750Sstevel@tonic-gate 			return (bufctl_walk_callback(cp, wsp, wsp->walk_addr));
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 		return (umem_walk_callback(wsp, (uintptr_t)buf));
11780Sstevel@tonic-gate 	}
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate 	ret = WALK_NEXT;
11810Sstevel@tonic-gate 
11820Sstevel@tonic-gate 	addr = umw->umw_addr;
11830Sstevel@tonic-gate 
11840Sstevel@tonic-gate 	/*
11850Sstevel@tonic-gate 	 * If we're walking freed buffers, report everything in the
11860Sstevel@tonic-gate 	 * magazine layer before processing the first slab.
11870Sstevel@tonic-gate 	 */
11880Sstevel@tonic-gate 	if ((type & UM_FREE) && magcnt != 0) {
11890Sstevel@tonic-gate 		umw->umw_count = 0;		/* only do this once */
11900Sstevel@tonic-gate 		for (i = 0; i < magcnt; i++) {
11910Sstevel@tonic-gate 			buf = maglist[i];
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate 			if (type & UM_BUFCTL) {
11940Sstevel@tonic-gate 				uintptr_t out;
11950Sstevel@tonic-gate 
11960Sstevel@tonic-gate 				if (cp->cache_flags & UMF_BUFTAG) {
11970Sstevel@tonic-gate 					umem_buftag_t *btp;
11980Sstevel@tonic-gate 					umem_buftag_t tag;
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate 					/* LINTED - alignment */
12010Sstevel@tonic-gate 					btp = UMEM_BUFTAG(cp, buf);
12020Sstevel@tonic-gate 					if (mdb_vread(&tag, sizeof (tag),
12030Sstevel@tonic-gate 					    (uintptr_t)btp) == -1) {
12040Sstevel@tonic-gate 						mdb_warn("reading buftag for "
12050Sstevel@tonic-gate 						    "%p at %p", buf, btp);
12060Sstevel@tonic-gate 						continue;
12070Sstevel@tonic-gate 					}
12080Sstevel@tonic-gate 					out = (uintptr_t)tag.bt_bufctl;
12090Sstevel@tonic-gate 				} else {
12100Sstevel@tonic-gate 					if (umem_hash_lookup(cp, addr, buf,
12110Sstevel@tonic-gate 					    &out) == -1)
12120Sstevel@tonic-gate 						continue;
12130Sstevel@tonic-gate 				}
12140Sstevel@tonic-gate 				ret = bufctl_walk_callback(cp, wsp, out);
12150Sstevel@tonic-gate 			} else {
12160Sstevel@tonic-gate 				ret = umem_walk_callback(wsp, (uintptr_t)buf);
12170Sstevel@tonic-gate 			}
12180Sstevel@tonic-gate 
12190Sstevel@tonic-gate 			if (ret != WALK_NEXT)
12200Sstevel@tonic-gate 				return (ret);
12210Sstevel@tonic-gate 		}
12220Sstevel@tonic-gate 	}
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate 	/*
12250Sstevel@tonic-gate 	 * Handle the buffers in the current slab
12260Sstevel@tonic-gate 	 */
12270Sstevel@tonic-gate 	chunksize = cp->cache_chunksize;
12280Sstevel@tonic-gate 	slabsize = cp->cache_slabsize;
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate 	sp = wsp->walk_layer;
12310Sstevel@tonic-gate 	chunks = sp->slab_chunks;
12320Sstevel@tonic-gate 	kbase = sp->slab_base;
12330Sstevel@tonic-gate 
12340Sstevel@tonic-gate 	dprintf(("kbase is %p\n", kbase));
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate 	if (!(cp->cache_flags & UMF_HASH)) {
12370Sstevel@tonic-gate 		valid = umw->umw_valid;
12380Sstevel@tonic-gate 		ubase = umw->umw_ubase;
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 		if (mdb_vread(ubase, chunks * chunksize,
12410Sstevel@tonic-gate 		    (uintptr_t)kbase) == -1) {
12420Sstevel@tonic-gate 			mdb_warn("failed to read slab contents at %p", kbase);
12430Sstevel@tonic-gate 			return (WALK_ERR);
12440Sstevel@tonic-gate 		}
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 		/*
12470Sstevel@tonic-gate 		 * Set up the valid map as fully allocated -- we'll punch
12480Sstevel@tonic-gate 		 * out the freelist.
12490Sstevel@tonic-gate 		 */
12500Sstevel@tonic-gate 		if (type & UM_ALLOCATED)
12510Sstevel@tonic-gate 			(void) memset(valid, 1, chunks);
12520Sstevel@tonic-gate 	} else {
12530Sstevel@tonic-gate 		valid = NULL;
12540Sstevel@tonic-gate 		ubase = NULL;
12550Sstevel@tonic-gate 	}
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 	/*
12580Sstevel@tonic-gate 	 * walk the slab's freelist
12590Sstevel@tonic-gate 	 */
12600Sstevel@tonic-gate 	bcp = sp->slab_head;
12610Sstevel@tonic-gate 
12620Sstevel@tonic-gate 	dprintf(("refcnt is %d; chunks is %d\n", sp->slab_refcnt, chunks));
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	/*
12650Sstevel@tonic-gate 	 * since we could be in the middle of allocating a buffer,
12660Sstevel@tonic-gate 	 * our refcnt could be one higher than it aught.  So we
12670Sstevel@tonic-gate 	 * check one further on the freelist than the count allows.
12680Sstevel@tonic-gate 	 */
12690Sstevel@tonic-gate 	for (i = sp->slab_refcnt; i <= chunks; i++) {
12700Sstevel@tonic-gate 		uint_t ndx;
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 		dprintf(("bcp is %p\n", bcp));
12730Sstevel@tonic-gate 
12740Sstevel@tonic-gate 		if (bcp == NULL) {
12750Sstevel@tonic-gate 			if (i == chunks)
12760Sstevel@tonic-gate 				break;
12770Sstevel@tonic-gate 			mdb_warn(
12780Sstevel@tonic-gate 			    "slab %p in cache %p freelist too short by %d\n",
12790Sstevel@tonic-gate 			    sp, addr, chunks - i);
12800Sstevel@tonic-gate 			break;
12810Sstevel@tonic-gate 		}
12820Sstevel@tonic-gate 
12830Sstevel@tonic-gate 		if (cp->cache_flags & UMF_HASH) {
12840Sstevel@tonic-gate 			if (mdb_vread(&bc, sizeof (bc), (uintptr_t)bcp) == -1) {
12850Sstevel@tonic-gate 				mdb_warn("failed to read bufctl ptr at %p",
12860Sstevel@tonic-gate 				    bcp);
12870Sstevel@tonic-gate 				break;
12880Sstevel@tonic-gate 			}
12890Sstevel@tonic-gate 			buf = bc.bc_addr;
12900Sstevel@tonic-gate 		} else {
12910Sstevel@tonic-gate 			/*
12920Sstevel@tonic-gate 			 * Otherwise the buffer is in the slab which
12930Sstevel@tonic-gate 			 * we've read in;  we just need to determine
12940Sstevel@tonic-gate 			 * its offset in the slab to find the
12950Sstevel@tonic-gate 			 * umem_bufctl_t.
12960Sstevel@tonic-gate 			 */
12970Sstevel@tonic-gate 			bc = *((umem_bufctl_t *)
12980Sstevel@tonic-gate 			    ((uintptr_t)bcp - (uintptr_t)kbase +
12990Sstevel@tonic-gate 			    (uintptr_t)ubase));
13000Sstevel@tonic-gate 
13010Sstevel@tonic-gate 			buf = UMEM_BUF(cp, bcp);
13020Sstevel@tonic-gate 		}
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate 		ndx = ((uintptr_t)buf - (uintptr_t)kbase) / chunksize;
13050Sstevel@tonic-gate 
13060Sstevel@tonic-gate 		if (ndx > slabsize / cp->cache_bufsize) {
13070Sstevel@tonic-gate 			/*
13080Sstevel@tonic-gate 			 * This is very wrong; we have managed to find
13090Sstevel@tonic-gate 			 * a buffer in the slab which shouldn't
13100Sstevel@tonic-gate 			 * actually be here.  Emit a warning, and
13110Sstevel@tonic-gate 			 * try to continue.
13120Sstevel@tonic-gate 			 */
13130Sstevel@tonic-gate 			mdb_warn("buf %p is out of range for "
13140Sstevel@tonic-gate 			    "slab %p, cache %p\n", buf, sp, addr);
13150Sstevel@tonic-gate 		} else if (type & UM_ALLOCATED) {
13160Sstevel@tonic-gate 			/*
13170Sstevel@tonic-gate 			 * we have found a buffer on the slab's freelist;
13180Sstevel@tonic-gate 			 * clear its entry
13190Sstevel@tonic-gate 			 */
13200Sstevel@tonic-gate 			valid[ndx] = 0;
13210Sstevel@tonic-gate 		} else {
13220Sstevel@tonic-gate 			/*
13230Sstevel@tonic-gate 			 * Report this freed buffer
13240Sstevel@tonic-gate 			 */
13250Sstevel@tonic-gate 			if (type & UM_BUFCTL) {
13260Sstevel@tonic-gate 				ret = bufctl_walk_callback(cp, wsp,
13270Sstevel@tonic-gate 				    (uintptr_t)bcp);
13280Sstevel@tonic-gate 			} else {
13290Sstevel@tonic-gate 				ret = umem_walk_callback(wsp, (uintptr_t)buf);
13300Sstevel@tonic-gate 			}
13310Sstevel@tonic-gate 			if (ret != WALK_NEXT)
13320Sstevel@tonic-gate 				return (ret);
13330Sstevel@tonic-gate 		}
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate 		bcp = bc.bc_next;
13360Sstevel@tonic-gate 	}
13370Sstevel@tonic-gate 
13380Sstevel@tonic-gate 	if (bcp != NULL) {
13390Sstevel@tonic-gate 		dprintf(("slab %p in cache %p freelist too long (%p)\n",
13400Sstevel@tonic-gate 		    sp, addr, bcp));
13410Sstevel@tonic-gate 	}
13420Sstevel@tonic-gate 
13430Sstevel@tonic-gate 	/*
13440Sstevel@tonic-gate 	 * If we are walking freed buffers, the loop above handled reporting
13450Sstevel@tonic-gate 	 * them.
13460Sstevel@tonic-gate 	 */
13470Sstevel@tonic-gate 	if (type & UM_FREE)
13480Sstevel@tonic-gate 		return (WALK_NEXT);
13490Sstevel@tonic-gate 
13500Sstevel@tonic-gate 	if (type & UM_BUFCTL) {
13510Sstevel@tonic-gate 		mdb_warn("impossible situation: small-slab UM_BUFCTL walk for "
13520Sstevel@tonic-gate 		    "cache %p\n", addr);
13530Sstevel@tonic-gate 		return (WALK_ERR);
13540Sstevel@tonic-gate 	}
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate 	/*
13570Sstevel@tonic-gate 	 * Report allocated buffers, skipping buffers in the magazine layer.
13580Sstevel@tonic-gate 	 * We only get this far for small-slab caches.
13590Sstevel@tonic-gate 	 */
13600Sstevel@tonic-gate 	for (i = 0; ret == WALK_NEXT && i < chunks; i++) {
13610Sstevel@tonic-gate 		buf = (char *)kbase + i * chunksize;
13620Sstevel@tonic-gate 
13630Sstevel@tonic-gate 		if (!valid[i])
13640Sstevel@tonic-gate 			continue;		/* on slab freelist */
13650Sstevel@tonic-gate 
13660Sstevel@tonic-gate 		if (magcnt > 0 &&
13670Sstevel@tonic-gate 		    bsearch(&buf, maglist, magcnt, sizeof (void *),
13680Sstevel@tonic-gate 		    addrcmp) != NULL)
13690Sstevel@tonic-gate 			continue;		/* in magazine layer */
13700Sstevel@tonic-gate 
13710Sstevel@tonic-gate 		ret = umem_walk_callback(wsp, (uintptr_t)buf);
13720Sstevel@tonic-gate 	}
13730Sstevel@tonic-gate 	return (ret);
13740Sstevel@tonic-gate }
13750Sstevel@tonic-gate 
13760Sstevel@tonic-gate void
13770Sstevel@tonic-gate umem_walk_fini(mdb_walk_state_t *wsp)
13780Sstevel@tonic-gate {
13790Sstevel@tonic-gate 	umem_walk_t *umw = wsp->walk_data;
13800Sstevel@tonic-gate 	uintptr_t chunksize;
13810Sstevel@tonic-gate 	uintptr_t slabsize;
13820Sstevel@tonic-gate 
13830Sstevel@tonic-gate 	if (umw == NULL)
13840Sstevel@tonic-gate 		return;
13850Sstevel@tonic-gate 
13860Sstevel@tonic-gate 	if (umw->umw_maglist != NULL)
13870Sstevel@tonic-gate 		mdb_free(umw->umw_maglist, umw->umw_max * sizeof (void *));
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate 	chunksize = umw->umw_cp->cache_chunksize;
13900Sstevel@tonic-gate 	slabsize = umw->umw_cp->cache_slabsize;
13910Sstevel@tonic-gate 
13920Sstevel@tonic-gate 	if (umw->umw_valid != NULL)
13930Sstevel@tonic-gate 		mdb_free(umw->umw_valid, slabsize / chunksize);
13940Sstevel@tonic-gate 	if (umw->umw_ubase != NULL)
13950Sstevel@tonic-gate 		mdb_free(umw->umw_ubase, slabsize + sizeof (umem_bufctl_t));
13960Sstevel@tonic-gate 
13970Sstevel@tonic-gate 	mdb_free(umw->umw_cp, umw->umw_csize);
13980Sstevel@tonic-gate 	mdb_free(umw, sizeof (umem_walk_t));
13990Sstevel@tonic-gate }
14000Sstevel@tonic-gate 
14010Sstevel@tonic-gate /*ARGSUSED*/
14020Sstevel@tonic-gate static int
14030Sstevel@tonic-gate umem_walk_all(uintptr_t addr, const umem_cache_t *c, mdb_walk_state_t *wsp)
14040Sstevel@tonic-gate {
14050Sstevel@tonic-gate 	/*
14060Sstevel@tonic-gate 	 * Buffers allocated from NOTOUCH caches can also show up as freed
14070Sstevel@tonic-gate 	 * memory in other caches.  This can be a little confusing, so we
14080Sstevel@tonic-gate 	 * don't walk NOTOUCH caches when walking all caches (thereby assuring
14090Sstevel@tonic-gate 	 * that "::walk umem" and "::walk freemem" yield disjoint output).
14100Sstevel@tonic-gate 	 */
14110Sstevel@tonic-gate 	if (c->cache_cflags & UMC_NOTOUCH)
14120Sstevel@tonic-gate 		return (WALK_NEXT);
14130Sstevel@tonic-gate 
14140Sstevel@tonic-gate 	if (mdb_pwalk(wsp->walk_data, wsp->walk_callback,
14150Sstevel@tonic-gate 	    wsp->walk_cbdata, addr) == -1)
14160Sstevel@tonic-gate 		return (WALK_DONE);
14170Sstevel@tonic-gate 
14180Sstevel@tonic-gate 	return (WALK_NEXT);
14190Sstevel@tonic-gate }
14200Sstevel@tonic-gate 
14210Sstevel@tonic-gate #define	UMEM_WALK_ALL(name, wsp) { \
14220Sstevel@tonic-gate 	wsp->walk_data = (name); \
14230Sstevel@tonic-gate 	if (mdb_walk("umem_cache", (mdb_walk_cb_t)umem_walk_all, wsp) == -1) \
14240Sstevel@tonic-gate 		return (WALK_ERR); \
14250Sstevel@tonic-gate 	return (WALK_DONE); \
14260Sstevel@tonic-gate }
14270Sstevel@tonic-gate 
14280Sstevel@tonic-gate int
14290Sstevel@tonic-gate umem_walk_init(mdb_walk_state_t *wsp)
14300Sstevel@tonic-gate {
14310Sstevel@tonic-gate 	if (wsp->walk_arg != NULL)
14320Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)wsp->walk_arg;
14330Sstevel@tonic-gate 
14340Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
14350Sstevel@tonic-gate 		UMEM_WALK_ALL("umem", wsp);
14360Sstevel@tonic-gate 	return (umem_walk_init_common(wsp, UM_ALLOCATED));
14370Sstevel@tonic-gate }
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate int
14400Sstevel@tonic-gate bufctl_walk_init(mdb_walk_state_t *wsp)
14410Sstevel@tonic-gate {
14420Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
14430Sstevel@tonic-gate 		UMEM_WALK_ALL("bufctl", wsp);
14440Sstevel@tonic-gate 	return (umem_walk_init_common(wsp, UM_ALLOCATED | UM_BUFCTL));
14450Sstevel@tonic-gate }
14460Sstevel@tonic-gate 
14470Sstevel@tonic-gate int
14480Sstevel@tonic-gate freemem_walk_init(mdb_walk_state_t *wsp)
14490Sstevel@tonic-gate {
14500Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
14510Sstevel@tonic-gate 		UMEM_WALK_ALL("freemem", wsp);
14520Sstevel@tonic-gate 	return (umem_walk_init_common(wsp, UM_FREE));
14530Sstevel@tonic-gate }
14540Sstevel@tonic-gate 
14550Sstevel@tonic-gate int
14560Sstevel@tonic-gate freectl_walk_init(mdb_walk_state_t *wsp)
14570Sstevel@tonic-gate {
14580Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
14590Sstevel@tonic-gate 		UMEM_WALK_ALL("freectl", wsp);
14600Sstevel@tonic-gate 	return (umem_walk_init_common(wsp, UM_FREE | UM_BUFCTL));
14610Sstevel@tonic-gate }
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate typedef struct bufctl_history_walk {
14640Sstevel@tonic-gate 	void		*bhw_next;
14650Sstevel@tonic-gate 	umem_cache_t	*bhw_cache;
14660Sstevel@tonic-gate 	umem_slab_t	*bhw_slab;
14670Sstevel@tonic-gate 	hrtime_t	bhw_timestamp;
14680Sstevel@tonic-gate } bufctl_history_walk_t;
14690Sstevel@tonic-gate 
14700Sstevel@tonic-gate int
14710Sstevel@tonic-gate bufctl_history_walk_init(mdb_walk_state_t *wsp)
14720Sstevel@tonic-gate {
14730Sstevel@tonic-gate 	bufctl_history_walk_t *bhw;
14740Sstevel@tonic-gate 	umem_bufctl_audit_t bc;
14750Sstevel@tonic-gate 	umem_bufctl_audit_t bcn;
14760Sstevel@tonic-gate 
14770Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
14780Sstevel@tonic-gate 		mdb_warn("bufctl_history walk doesn't support global walks\n");
14790Sstevel@tonic-gate 		return (WALK_ERR);
14800Sstevel@tonic-gate 	}
14810Sstevel@tonic-gate 
14820Sstevel@tonic-gate 	if (mdb_vread(&bc, sizeof (bc), wsp->walk_addr) == -1) {
14830Sstevel@tonic-gate 		mdb_warn("unable to read bufctl at %p", wsp->walk_addr);
14840Sstevel@tonic-gate 		return (WALK_ERR);
14850Sstevel@tonic-gate 	}
14860Sstevel@tonic-gate 
14870Sstevel@tonic-gate 	bhw = mdb_zalloc(sizeof (*bhw), UM_SLEEP);
14880Sstevel@tonic-gate 	bhw->bhw_timestamp = 0;
14890Sstevel@tonic-gate 	bhw->bhw_cache = bc.bc_cache;
14900Sstevel@tonic-gate 	bhw->bhw_slab = bc.bc_slab;
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 	/*
14930Sstevel@tonic-gate 	 * sometimes the first log entry matches the base bufctl;  in that
14940Sstevel@tonic-gate 	 * case, skip the base bufctl.
14950Sstevel@tonic-gate 	 */
14960Sstevel@tonic-gate 	if (bc.bc_lastlog != NULL &&
14970Sstevel@tonic-gate 	    mdb_vread(&bcn, sizeof (bcn), (uintptr_t)bc.bc_lastlog) != -1 &&
14980Sstevel@tonic-gate 	    bc.bc_addr == bcn.bc_addr &&
14990Sstevel@tonic-gate 	    bc.bc_cache == bcn.bc_cache &&
15000Sstevel@tonic-gate 	    bc.bc_slab == bcn.bc_slab &&
15010Sstevel@tonic-gate 	    bc.bc_timestamp == bcn.bc_timestamp &&
15020Sstevel@tonic-gate 	    bc.bc_thread == bcn.bc_thread)
15030Sstevel@tonic-gate 		bhw->bhw_next = bc.bc_lastlog;
15040Sstevel@tonic-gate 	else
15050Sstevel@tonic-gate 		bhw->bhw_next = (void *)wsp->walk_addr;
15060Sstevel@tonic-gate 
15070Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)bc.bc_addr;
15080Sstevel@tonic-gate 	wsp->walk_data = bhw;
15090Sstevel@tonic-gate 
15100Sstevel@tonic-gate 	return (WALK_NEXT);
15110Sstevel@tonic-gate }
15120Sstevel@tonic-gate 
15130Sstevel@tonic-gate int
15140Sstevel@tonic-gate bufctl_history_walk_step(mdb_walk_state_t *wsp)
15150Sstevel@tonic-gate {
15160Sstevel@tonic-gate 	bufctl_history_walk_t *bhw = wsp->walk_data;
15170Sstevel@tonic-gate 	uintptr_t addr = (uintptr_t)bhw->bhw_next;
15180Sstevel@tonic-gate 	uintptr_t baseaddr = wsp->walk_addr;
15190Sstevel@tonic-gate 	umem_bufctl_audit_t *b;
15200Sstevel@tonic-gate 	UMEM_LOCAL_BUFCTL_AUDIT(&b);
15210Sstevel@tonic-gate 
15220Sstevel@tonic-gate 	if (addr == NULL)
15230Sstevel@tonic-gate 		return (WALK_DONE);
15240Sstevel@tonic-gate 
15250Sstevel@tonic-gate 	if (mdb_vread(b, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) {
15260Sstevel@tonic-gate 		mdb_warn("unable to read bufctl at %p", bhw->bhw_next);
15270Sstevel@tonic-gate 		return (WALK_ERR);
15280Sstevel@tonic-gate 	}
15290Sstevel@tonic-gate 
15300Sstevel@tonic-gate 	/*
15310Sstevel@tonic-gate 	 * The bufctl is only valid if the address, cache, and slab are
15320Sstevel@tonic-gate 	 * correct.  We also check that the timestamp is decreasing, to
15330Sstevel@tonic-gate 	 * prevent infinite loops.
15340Sstevel@tonic-gate 	 */
15350Sstevel@tonic-gate 	if ((uintptr_t)b->bc_addr != baseaddr ||
15360Sstevel@tonic-gate 	    b->bc_cache != bhw->bhw_cache ||
15370Sstevel@tonic-gate 	    b->bc_slab != bhw->bhw_slab ||
15380Sstevel@tonic-gate 	    (bhw->bhw_timestamp != 0 && b->bc_timestamp >= bhw->bhw_timestamp))
15390Sstevel@tonic-gate 		return (WALK_DONE);
15400Sstevel@tonic-gate 
15410Sstevel@tonic-gate 	bhw->bhw_next = b->bc_lastlog;
15420Sstevel@tonic-gate 	bhw->bhw_timestamp = b->bc_timestamp;
15430Sstevel@tonic-gate 
15440Sstevel@tonic-gate 	return (wsp->walk_callback(addr, b, wsp->walk_cbdata));
15450Sstevel@tonic-gate }
15460Sstevel@tonic-gate 
15470Sstevel@tonic-gate void
15480Sstevel@tonic-gate bufctl_history_walk_fini(mdb_walk_state_t *wsp)
15490Sstevel@tonic-gate {
15500Sstevel@tonic-gate 	bufctl_history_walk_t *bhw = wsp->walk_data;
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate 	mdb_free(bhw, sizeof (*bhw));
15530Sstevel@tonic-gate }
15540Sstevel@tonic-gate 
15550Sstevel@tonic-gate typedef struct umem_log_walk {
15560Sstevel@tonic-gate 	umem_bufctl_audit_t *ulw_base;
15570Sstevel@tonic-gate 	umem_bufctl_audit_t **ulw_sorted;
15580Sstevel@tonic-gate 	umem_log_header_t ulw_lh;
15590Sstevel@tonic-gate 	size_t ulw_size;
15600Sstevel@tonic-gate 	size_t ulw_maxndx;
15610Sstevel@tonic-gate 	size_t ulw_ndx;
15620Sstevel@tonic-gate } umem_log_walk_t;
15630Sstevel@tonic-gate 
15640Sstevel@tonic-gate int
15650Sstevel@tonic-gate umem_log_walk_init(mdb_walk_state_t *wsp)
15660Sstevel@tonic-gate {
15670Sstevel@tonic-gate 	uintptr_t lp = wsp->walk_addr;
15680Sstevel@tonic-gate 	umem_log_walk_t *ulw;
15690Sstevel@tonic-gate 	umem_log_header_t *lhp;
15700Sstevel@tonic-gate 	int maxndx, i, j, k;
15710Sstevel@tonic-gate 
15720Sstevel@tonic-gate 	/*
15730Sstevel@tonic-gate 	 * By default (global walk), walk the umem_transaction_log.  Otherwise
15740Sstevel@tonic-gate 	 * read the log whose umem_log_header_t is stored at walk_addr.
15750Sstevel@tonic-gate 	 */
15760Sstevel@tonic-gate 	if (lp == NULL && umem_readvar(&lp, "umem_transaction_log") == -1) {
15770Sstevel@tonic-gate 		mdb_warn("failed to read 'umem_transaction_log'");
15780Sstevel@tonic-gate 		return (WALK_ERR);
15790Sstevel@tonic-gate 	}
15800Sstevel@tonic-gate 
15810Sstevel@tonic-gate 	if (lp == NULL) {
15820Sstevel@tonic-gate 		mdb_warn("log is disabled\n");
15830Sstevel@tonic-gate 		return (WALK_ERR);
15840Sstevel@tonic-gate 	}
15850Sstevel@tonic-gate 
15860Sstevel@tonic-gate 	ulw = mdb_zalloc(sizeof (umem_log_walk_t), UM_SLEEP);
15870Sstevel@tonic-gate 	lhp = &ulw->ulw_lh;
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate 	if (mdb_vread(lhp, sizeof (umem_log_header_t), lp) == -1) {
15900Sstevel@tonic-gate 		mdb_warn("failed to read log header at %p", lp);
15910Sstevel@tonic-gate 		mdb_free(ulw, sizeof (umem_log_walk_t));
15920Sstevel@tonic-gate 		return (WALK_ERR);
15930Sstevel@tonic-gate 	}
15940Sstevel@tonic-gate 
15950Sstevel@tonic-gate 	ulw->ulw_size = lhp->lh_chunksize * lhp->lh_nchunks;
15960Sstevel@tonic-gate 	ulw->ulw_base = mdb_alloc(ulw->ulw_size, UM_SLEEP);
15970Sstevel@tonic-gate 	maxndx = lhp->lh_chunksize / UMEM_BUFCTL_AUDIT_SIZE - 1;
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate 	if (mdb_vread(ulw->ulw_base, ulw->ulw_size,
16000Sstevel@tonic-gate 	    (uintptr_t)lhp->lh_base) == -1) {
16010Sstevel@tonic-gate 		mdb_warn("failed to read log at base %p", lhp->lh_base);
16020Sstevel@tonic-gate 		mdb_free(ulw->ulw_base, ulw->ulw_size);
16030Sstevel@tonic-gate 		mdb_free(ulw, sizeof (umem_log_walk_t));
16040Sstevel@tonic-gate 		return (WALK_ERR);
16050Sstevel@tonic-gate 	}
16060Sstevel@tonic-gate 
16070Sstevel@tonic-gate 	ulw->ulw_sorted = mdb_alloc(maxndx * lhp->lh_nchunks *
16080Sstevel@tonic-gate 	    sizeof (umem_bufctl_audit_t *), UM_SLEEP);
16090Sstevel@tonic-gate 
16100Sstevel@tonic-gate 	for (i = 0, k = 0; i < lhp->lh_nchunks; i++) {
16110Sstevel@tonic-gate 		caddr_t chunk = (caddr_t)
16120Sstevel@tonic-gate 		    ((uintptr_t)ulw->ulw_base + i * lhp->lh_chunksize);
16130Sstevel@tonic-gate 
16140Sstevel@tonic-gate 		for (j = 0; j < maxndx; j++) {
16150Sstevel@tonic-gate 			/* LINTED align */
16160Sstevel@tonic-gate 			ulw->ulw_sorted[k++] = (umem_bufctl_audit_t *)chunk;
16170Sstevel@tonic-gate 			chunk += UMEM_BUFCTL_AUDIT_SIZE;
16180Sstevel@tonic-gate 		}
16190Sstevel@tonic-gate 	}
16200Sstevel@tonic-gate 
16210Sstevel@tonic-gate 	qsort(ulw->ulw_sorted, k, sizeof (umem_bufctl_audit_t *),
16220Sstevel@tonic-gate 	    (int(*)(const void *, const void *))bufctlcmp);
16230Sstevel@tonic-gate 
16240Sstevel@tonic-gate 	ulw->ulw_maxndx = k;
16250Sstevel@tonic-gate 	wsp->walk_data = ulw;
16260Sstevel@tonic-gate 
16270Sstevel@tonic-gate 	return (WALK_NEXT);
16280Sstevel@tonic-gate }
16290Sstevel@tonic-gate 
16300Sstevel@tonic-gate int
16310Sstevel@tonic-gate umem_log_walk_step(mdb_walk_state_t *wsp)
16320Sstevel@tonic-gate {
16330Sstevel@tonic-gate 	umem_log_walk_t *ulw = wsp->walk_data;
16340Sstevel@tonic-gate 	umem_bufctl_audit_t *bcp;
16350Sstevel@tonic-gate 
16360Sstevel@tonic-gate 	if (ulw->ulw_ndx == ulw->ulw_maxndx)
16370Sstevel@tonic-gate 		return (WALK_DONE);
16380Sstevel@tonic-gate 
16390Sstevel@tonic-gate 	bcp = ulw->ulw_sorted[ulw->ulw_ndx++];
16400Sstevel@tonic-gate 
16410Sstevel@tonic-gate 	return (wsp->walk_callback((uintptr_t)bcp - (uintptr_t)ulw->ulw_base +
16420Sstevel@tonic-gate 	    (uintptr_t)ulw->ulw_lh.lh_base, bcp, wsp->walk_cbdata));
16430Sstevel@tonic-gate }
16440Sstevel@tonic-gate 
16450Sstevel@tonic-gate void
16460Sstevel@tonic-gate umem_log_walk_fini(mdb_walk_state_t *wsp)
16470Sstevel@tonic-gate {
16480Sstevel@tonic-gate 	umem_log_walk_t *ulw = wsp->walk_data;
16490Sstevel@tonic-gate 
16500Sstevel@tonic-gate 	mdb_free(ulw->ulw_base, ulw->ulw_size);
16510Sstevel@tonic-gate 	mdb_free(ulw->ulw_sorted, ulw->ulw_maxndx *
16520Sstevel@tonic-gate 	    sizeof (umem_bufctl_audit_t *));
16530Sstevel@tonic-gate 	mdb_free(ulw, sizeof (umem_log_walk_t));
16540Sstevel@tonic-gate }
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate typedef struct allocdby_bufctl {
16570Sstevel@tonic-gate 	uintptr_t abb_addr;
16580Sstevel@tonic-gate 	hrtime_t abb_ts;
16590Sstevel@tonic-gate } allocdby_bufctl_t;
16600Sstevel@tonic-gate 
16610Sstevel@tonic-gate typedef struct allocdby_walk {
16620Sstevel@tonic-gate 	const char *abw_walk;
16630Sstevel@tonic-gate 	uintptr_t abw_thread;
16640Sstevel@tonic-gate 	size_t abw_nbufs;
16650Sstevel@tonic-gate 	size_t abw_size;
16660Sstevel@tonic-gate 	allocdby_bufctl_t *abw_buf;
16670Sstevel@tonic-gate 	size_t abw_ndx;
16680Sstevel@tonic-gate } allocdby_walk_t;
16690Sstevel@tonic-gate 
16700Sstevel@tonic-gate int
16710Sstevel@tonic-gate allocdby_walk_bufctl(uintptr_t addr, const umem_bufctl_audit_t *bcp,
16720Sstevel@tonic-gate     allocdby_walk_t *abw)
16730Sstevel@tonic-gate {
16740Sstevel@tonic-gate 	if ((uintptr_t)bcp->bc_thread != abw->abw_thread)
16750Sstevel@tonic-gate 		return (WALK_NEXT);
16760Sstevel@tonic-gate 
16770Sstevel@tonic-gate 	if (abw->abw_nbufs == abw->abw_size) {
16780Sstevel@tonic-gate 		allocdby_bufctl_t *buf;
16790Sstevel@tonic-gate 		size_t oldsize = sizeof (allocdby_bufctl_t) * abw->abw_size;
16800Sstevel@tonic-gate 
16810Sstevel@tonic-gate 		buf = mdb_zalloc(oldsize << 1, UM_SLEEP);
16820Sstevel@tonic-gate 
16830Sstevel@tonic-gate 		bcopy(abw->abw_buf, buf, oldsize);
16840Sstevel@tonic-gate 		mdb_free(abw->abw_buf, oldsize);
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate 		abw->abw_size <<= 1;
16870Sstevel@tonic-gate 		abw->abw_buf = buf;
16880Sstevel@tonic-gate 	}
16890Sstevel@tonic-gate 
16900Sstevel@tonic-gate 	abw->abw_buf[abw->abw_nbufs].abb_addr = addr;
16910Sstevel@tonic-gate 	abw->abw_buf[abw->abw_nbufs].abb_ts = bcp->bc_timestamp;
16920Sstevel@tonic-gate 	abw->abw_nbufs++;
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate 	return (WALK_NEXT);
16950Sstevel@tonic-gate }
16960Sstevel@tonic-gate 
16970Sstevel@tonic-gate /*ARGSUSED*/
16980Sstevel@tonic-gate int
16990Sstevel@tonic-gate allocdby_walk_cache(uintptr_t addr, const umem_cache_t *c, allocdby_walk_t *abw)
17000Sstevel@tonic-gate {
17010Sstevel@tonic-gate 	if (mdb_pwalk(abw->abw_walk, (mdb_walk_cb_t)allocdby_walk_bufctl,
17020Sstevel@tonic-gate 	    abw, addr) == -1) {
17030Sstevel@tonic-gate 		mdb_warn("couldn't walk bufctl for cache %p", addr);
17040Sstevel@tonic-gate 		return (WALK_DONE);
17050Sstevel@tonic-gate 	}
17060Sstevel@tonic-gate 
17070Sstevel@tonic-gate 	return (WALK_NEXT);
17080Sstevel@tonic-gate }
17090Sstevel@tonic-gate 
17100Sstevel@tonic-gate static int
17110Sstevel@tonic-gate allocdby_cmp(const allocdby_bufctl_t *lhs, const allocdby_bufctl_t *rhs)
17120Sstevel@tonic-gate {
17130Sstevel@tonic-gate 	if (lhs->abb_ts < rhs->abb_ts)
17140Sstevel@tonic-gate 		return (1);
17150Sstevel@tonic-gate 	if (lhs->abb_ts > rhs->abb_ts)
17160Sstevel@tonic-gate 		return (-1);
17170Sstevel@tonic-gate 	return (0);
17180Sstevel@tonic-gate }
17190Sstevel@tonic-gate 
17200Sstevel@tonic-gate static int
17210Sstevel@tonic-gate allocdby_walk_init_common(mdb_walk_state_t *wsp, const char *walk)
17220Sstevel@tonic-gate {
17230Sstevel@tonic-gate 	allocdby_walk_t *abw;
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
17260Sstevel@tonic-gate 		mdb_warn("allocdby walk doesn't support global walks\n");
17270Sstevel@tonic-gate 		return (WALK_ERR);
17280Sstevel@tonic-gate 	}
17290Sstevel@tonic-gate 
17300Sstevel@tonic-gate 	abw = mdb_zalloc(sizeof (allocdby_walk_t), UM_SLEEP);
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate 	abw->abw_thread = wsp->walk_addr;
17330Sstevel@tonic-gate 	abw->abw_walk = walk;
17340Sstevel@tonic-gate 	abw->abw_size = 128;	/* something reasonable */
17350Sstevel@tonic-gate 	abw->abw_buf =
17360Sstevel@tonic-gate 	    mdb_zalloc(abw->abw_size * sizeof (allocdby_bufctl_t), UM_SLEEP);
17370Sstevel@tonic-gate 
17380Sstevel@tonic-gate 	wsp->walk_data = abw;
17390Sstevel@tonic-gate 
17400Sstevel@tonic-gate 	if (mdb_walk("umem_cache",
17410Sstevel@tonic-gate 	    (mdb_walk_cb_t)allocdby_walk_cache, abw) == -1) {
17420Sstevel@tonic-gate 		mdb_warn("couldn't walk umem_cache");
17430Sstevel@tonic-gate 		allocdby_walk_fini(wsp);
17440Sstevel@tonic-gate 		return (WALK_ERR);
17450Sstevel@tonic-gate 	}
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 	qsort(abw->abw_buf, abw->abw_nbufs, sizeof (allocdby_bufctl_t),
17480Sstevel@tonic-gate 	    (int(*)(const void *, const void *))allocdby_cmp);
17490Sstevel@tonic-gate 
17500Sstevel@tonic-gate 	return (WALK_NEXT);
17510Sstevel@tonic-gate }
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate int
17540Sstevel@tonic-gate allocdby_walk_init(mdb_walk_state_t *wsp)
17550Sstevel@tonic-gate {
17560Sstevel@tonic-gate 	return (allocdby_walk_init_common(wsp, "bufctl"));
17570Sstevel@tonic-gate }
17580Sstevel@tonic-gate 
17590Sstevel@tonic-gate int
17600Sstevel@tonic-gate freedby_walk_init(mdb_walk_state_t *wsp)
17610Sstevel@tonic-gate {
17620Sstevel@tonic-gate 	return (allocdby_walk_init_common(wsp, "freectl"));
17630Sstevel@tonic-gate }
17640Sstevel@tonic-gate 
17650Sstevel@tonic-gate int
17660Sstevel@tonic-gate allocdby_walk_step(mdb_walk_state_t *wsp)
17670Sstevel@tonic-gate {
17680Sstevel@tonic-gate 	allocdby_walk_t *abw = wsp->walk_data;
17690Sstevel@tonic-gate 	uintptr_t addr;
17700Sstevel@tonic-gate 	umem_bufctl_audit_t *bcp;
17710Sstevel@tonic-gate 	UMEM_LOCAL_BUFCTL_AUDIT(&bcp);
17720Sstevel@tonic-gate 
17730Sstevel@tonic-gate 	if (abw->abw_ndx == abw->abw_nbufs)
17740Sstevel@tonic-gate 		return (WALK_DONE);
17750Sstevel@tonic-gate 
17760Sstevel@tonic-gate 	addr = abw->abw_buf[abw->abw_ndx++].abb_addr;
17770Sstevel@tonic-gate 
17780Sstevel@tonic-gate 	if (mdb_vread(bcp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) {
17790Sstevel@tonic-gate 		mdb_warn("couldn't read bufctl at %p", addr);
17800Sstevel@tonic-gate 		return (WALK_DONE);
17810Sstevel@tonic-gate 	}
17820Sstevel@tonic-gate 
17830Sstevel@tonic-gate 	return (wsp->walk_callback(addr, bcp, wsp->walk_cbdata));
17840Sstevel@tonic-gate }
17850Sstevel@tonic-gate 
17860Sstevel@tonic-gate void
17870Sstevel@tonic-gate allocdby_walk_fini(mdb_walk_state_t *wsp)
17880Sstevel@tonic-gate {
17890Sstevel@tonic-gate 	allocdby_walk_t *abw = wsp->walk_data;
17900Sstevel@tonic-gate 
17910Sstevel@tonic-gate 	mdb_free(abw->abw_buf, sizeof (allocdby_bufctl_t) * abw->abw_size);
17920Sstevel@tonic-gate 	mdb_free(abw, sizeof (allocdby_walk_t));
17930Sstevel@tonic-gate }
17940Sstevel@tonic-gate 
17950Sstevel@tonic-gate /*ARGSUSED*/
17960Sstevel@tonic-gate int
17970Sstevel@tonic-gate allocdby_walk(uintptr_t addr, const umem_bufctl_audit_t *bcp, void *ignored)
17980Sstevel@tonic-gate {
17990Sstevel@tonic-gate 	char c[MDB_SYM_NAMLEN];
18000Sstevel@tonic-gate 	GElf_Sym sym;
18010Sstevel@tonic-gate 	int i;
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate 	mdb_printf("%0?p %12llx ", addr, bcp->bc_timestamp);
18040Sstevel@tonic-gate 	for (i = 0; i < bcp->bc_depth; i++) {
18050Sstevel@tonic-gate 		if (mdb_lookup_by_addr(bcp->bc_stack[i],
18060Sstevel@tonic-gate 		    MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1)
18070Sstevel@tonic-gate 			continue;
18080Sstevel@tonic-gate 		if (is_umem_sym(c, "umem_"))
18090Sstevel@tonic-gate 			continue;
18100Sstevel@tonic-gate 		mdb_printf("%s+0x%lx",
18110Sstevel@tonic-gate 		    c, bcp->bc_stack[i] - (uintptr_t)sym.st_value);
18120Sstevel@tonic-gate 		break;
18130Sstevel@tonic-gate 	}
18140Sstevel@tonic-gate 	mdb_printf("\n");
18150Sstevel@tonic-gate 
18160Sstevel@tonic-gate 	return (WALK_NEXT);
18170Sstevel@tonic-gate }
18180Sstevel@tonic-gate 
18190Sstevel@tonic-gate static int
18200Sstevel@tonic-gate allocdby_common(uintptr_t addr, uint_t flags, const char *w)
18210Sstevel@tonic-gate {
18220Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
18230Sstevel@tonic-gate 		return (DCMD_USAGE);
18240Sstevel@tonic-gate 
18250Sstevel@tonic-gate 	mdb_printf("%-?s %12s %s\n", "BUFCTL", "TIMESTAMP", "CALLER");
18260Sstevel@tonic-gate 
18270Sstevel@tonic-gate 	if (mdb_pwalk(w, (mdb_walk_cb_t)allocdby_walk, NULL, addr) == -1) {
18280Sstevel@tonic-gate 		mdb_warn("can't walk '%s' for %p", w, addr);
18290Sstevel@tonic-gate 		return (DCMD_ERR);
18300Sstevel@tonic-gate 	}
18310Sstevel@tonic-gate 
18320Sstevel@tonic-gate 	return (DCMD_OK);
18330Sstevel@tonic-gate }
18340Sstevel@tonic-gate 
18350Sstevel@tonic-gate /*ARGSUSED*/
18360Sstevel@tonic-gate int
18370Sstevel@tonic-gate allocdby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
18380Sstevel@tonic-gate {
18390Sstevel@tonic-gate 	return (allocdby_common(addr, flags, "allocdby"));
18400Sstevel@tonic-gate }
18410Sstevel@tonic-gate 
18420Sstevel@tonic-gate /*ARGSUSED*/
18430Sstevel@tonic-gate int
18440Sstevel@tonic-gate freedby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
18450Sstevel@tonic-gate {
18460Sstevel@tonic-gate 	return (allocdby_common(addr, flags, "freedby"));
18470Sstevel@tonic-gate }
18480Sstevel@tonic-gate 
18490Sstevel@tonic-gate typedef struct whatis {
18500Sstevel@tonic-gate 	uintptr_t w_addr;
18510Sstevel@tonic-gate 	const umem_cache_t *w_cache;
18520Sstevel@tonic-gate 	const vmem_t *w_vmem;
18530Sstevel@tonic-gate 	int w_found;
18540Sstevel@tonic-gate 	uint_t w_verbose;
18550Sstevel@tonic-gate 	uint_t w_freemem;
18560Sstevel@tonic-gate 	uint_t w_all;
18570Sstevel@tonic-gate 	uint_t w_bufctl;
18580Sstevel@tonic-gate } whatis_t;
18590Sstevel@tonic-gate 
18600Sstevel@tonic-gate static void
18610Sstevel@tonic-gate whatis_print_umem(uintptr_t addr, uintptr_t baddr, whatis_t *w)
18620Sstevel@tonic-gate {
18630Sstevel@tonic-gate 	/* LINTED pointer cast may result in improper alignment */
18640Sstevel@tonic-gate 	uintptr_t btaddr = (uintptr_t)UMEM_BUFTAG(w->w_cache, addr);
18650Sstevel@tonic-gate 	intptr_t stat;
18660Sstevel@tonic-gate 
18670Sstevel@tonic-gate 	if (w->w_cache->cache_flags & UMF_REDZONE) {
18680Sstevel@tonic-gate 		umem_buftag_t bt;
18690Sstevel@tonic-gate 
18700Sstevel@tonic-gate 		if (mdb_vread(&bt, sizeof (bt), btaddr) == -1)
18710Sstevel@tonic-gate 			goto done;
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate 		stat = (intptr_t)bt.bt_bufctl ^ bt.bt_bxstat;
18740Sstevel@tonic-gate 
18750Sstevel@tonic-gate 		if (stat != UMEM_BUFTAG_ALLOC && stat != UMEM_BUFTAG_FREE)
18760Sstevel@tonic-gate 			goto done;
18770Sstevel@tonic-gate 
18780Sstevel@tonic-gate 		/*
18790Sstevel@tonic-gate 		 * provide the bufctl ptr if it has useful information
18800Sstevel@tonic-gate 		 */
18810Sstevel@tonic-gate 		if (baddr == 0 && (w->w_cache->cache_flags & UMF_AUDIT))
18820Sstevel@tonic-gate 			baddr = (uintptr_t)bt.bt_bufctl;
18830Sstevel@tonic-gate 	}
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate done:
18860Sstevel@tonic-gate 	if (baddr == 0)
18870Sstevel@tonic-gate 		mdb_printf("%p is %p+%p, %s from %s\n",
18880Sstevel@tonic-gate 		    w->w_addr, addr, w->w_addr - addr,
18890Sstevel@tonic-gate 		    w->w_freemem == FALSE ? "allocated" : "freed",
18900Sstevel@tonic-gate 		    w->w_cache->cache_name);
18910Sstevel@tonic-gate 	else
18920Sstevel@tonic-gate 		mdb_printf("%p is %p+%p, bufctl %p %s from %s\n",
18930Sstevel@tonic-gate 		    w->w_addr, addr, w->w_addr - addr, baddr,
18940Sstevel@tonic-gate 		    w->w_freemem == FALSE ? "allocated" : "freed",
18950Sstevel@tonic-gate 		    w->w_cache->cache_name);
18960Sstevel@tonic-gate }
18970Sstevel@tonic-gate 
18980Sstevel@tonic-gate /*ARGSUSED*/
18990Sstevel@tonic-gate static int
19000Sstevel@tonic-gate whatis_walk_umem(uintptr_t addr, void *ignored, whatis_t *w)
19010Sstevel@tonic-gate {
19020Sstevel@tonic-gate 	if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize)
19030Sstevel@tonic-gate 		return (WALK_NEXT);
19040Sstevel@tonic-gate 
19050Sstevel@tonic-gate 	whatis_print_umem(addr, 0, w);
19060Sstevel@tonic-gate 	w->w_found++;
19070Sstevel@tonic-gate 	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
19080Sstevel@tonic-gate }
19090Sstevel@tonic-gate 
19100Sstevel@tonic-gate static int
19110Sstevel@tonic-gate whatis_walk_seg(uintptr_t addr, const vmem_seg_t *vs, whatis_t *w)
19120Sstevel@tonic-gate {
19130Sstevel@tonic-gate 	if (w->w_addr < vs->vs_start || w->w_addr >= vs->vs_end)
19140Sstevel@tonic-gate 		return (WALK_NEXT);
19150Sstevel@tonic-gate 
19160Sstevel@tonic-gate 	mdb_printf("%p is %p+%p ", w->w_addr,
19170Sstevel@tonic-gate 	    vs->vs_start, w->w_addr - vs->vs_start);
19180Sstevel@tonic-gate 
19190Sstevel@tonic-gate 	/*
19200Sstevel@tonic-gate 	 * Always provide the vmem_seg pointer if it has a stack trace.
19210Sstevel@tonic-gate 	 */
19220Sstevel@tonic-gate 	if (w->w_bufctl == TRUE ||
19230Sstevel@tonic-gate 	    (vs->vs_type == VMEM_ALLOC && vs->vs_depth != 0)) {
19240Sstevel@tonic-gate 		mdb_printf("(vmem_seg %p) ", addr);
19250Sstevel@tonic-gate 	}
19260Sstevel@tonic-gate 
19270Sstevel@tonic-gate 	mdb_printf("%sfrom %s vmem arena\n", w->w_freemem == TRUE ?
19280Sstevel@tonic-gate 	    "freed " : "", w->w_vmem->vm_name);
19290Sstevel@tonic-gate 
19300Sstevel@tonic-gate 	w->w_found++;
19310Sstevel@tonic-gate 	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
19320Sstevel@tonic-gate }
19330Sstevel@tonic-gate 
19340Sstevel@tonic-gate static int
19350Sstevel@tonic-gate whatis_walk_vmem(uintptr_t addr, const vmem_t *vmem, whatis_t *w)
19360Sstevel@tonic-gate {
19370Sstevel@tonic-gate 	const char *nm = vmem->vm_name;
19380Sstevel@tonic-gate 	w->w_vmem = vmem;
19390Sstevel@tonic-gate 	w->w_freemem = FALSE;
19400Sstevel@tonic-gate 
19410Sstevel@tonic-gate 	if (w->w_verbose)
19420Sstevel@tonic-gate 		mdb_printf("Searching vmem arena %s...\n", nm);
19430Sstevel@tonic-gate 
19440Sstevel@tonic-gate 	if (mdb_pwalk("vmem_alloc",
19450Sstevel@tonic-gate 	    (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) {
19460Sstevel@tonic-gate 		mdb_warn("can't walk vmem seg for %p", addr);
19470Sstevel@tonic-gate 		return (WALK_NEXT);
19480Sstevel@tonic-gate 	}
19490Sstevel@tonic-gate 
19500Sstevel@tonic-gate 	if (w->w_found && w->w_all == FALSE)
19510Sstevel@tonic-gate 		return (WALK_DONE);
19520Sstevel@tonic-gate 
19530Sstevel@tonic-gate 	if (w->w_verbose)
19540Sstevel@tonic-gate 		mdb_printf("Searching vmem arena %s for free virtual...\n", nm);
19550Sstevel@tonic-gate 
19560Sstevel@tonic-gate 	w->w_freemem = TRUE;
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	if (mdb_pwalk("vmem_free",
19590Sstevel@tonic-gate 	    (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) {
19600Sstevel@tonic-gate 		mdb_warn("can't walk vmem seg for %p", addr);
19610Sstevel@tonic-gate 		return (WALK_NEXT);
19620Sstevel@tonic-gate 	}
19630Sstevel@tonic-gate 
19640Sstevel@tonic-gate 	return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT);
19650Sstevel@tonic-gate }
19660Sstevel@tonic-gate 
19670Sstevel@tonic-gate /*ARGSUSED*/
19680Sstevel@tonic-gate static int
19690Sstevel@tonic-gate whatis_walk_bufctl(uintptr_t baddr, const umem_bufctl_t *bcp, whatis_t *w)
19700Sstevel@tonic-gate {
19710Sstevel@tonic-gate 	uintptr_t addr;
19720Sstevel@tonic-gate 
19730Sstevel@tonic-gate 	if (bcp == NULL)
19740Sstevel@tonic-gate 		return (WALK_NEXT);
19750Sstevel@tonic-gate 
19760Sstevel@tonic-gate 	addr = (uintptr_t)bcp->bc_addr;
19770Sstevel@tonic-gate 
19780Sstevel@tonic-gate 	if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize)
19790Sstevel@tonic-gate 		return (WALK_NEXT);
19800Sstevel@tonic-gate 
19810Sstevel@tonic-gate 	whatis_print_umem(addr, baddr, w);
19820Sstevel@tonic-gate 	w->w_found++;
19830Sstevel@tonic-gate 	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
19840Sstevel@tonic-gate }
19850Sstevel@tonic-gate 
19860Sstevel@tonic-gate static int
19870Sstevel@tonic-gate whatis_walk_cache(uintptr_t addr, const umem_cache_t *c, whatis_t *w)
19880Sstevel@tonic-gate {
19890Sstevel@tonic-gate 	char *walk, *freewalk;
19900Sstevel@tonic-gate 	mdb_walk_cb_t func;
19910Sstevel@tonic-gate 
19920Sstevel@tonic-gate 	if (w->w_bufctl == FALSE) {
19930Sstevel@tonic-gate 		walk = "umem";
19940Sstevel@tonic-gate 		freewalk = "freemem";
19950Sstevel@tonic-gate 		func = (mdb_walk_cb_t)whatis_walk_umem;
19960Sstevel@tonic-gate 	} else {
19970Sstevel@tonic-gate 		walk = "bufctl";
19980Sstevel@tonic-gate 		freewalk = "freectl";
19990Sstevel@tonic-gate 		func = (mdb_walk_cb_t)whatis_walk_bufctl;
20000Sstevel@tonic-gate 	}
20010Sstevel@tonic-gate 
20020Sstevel@tonic-gate 	if (w->w_verbose)
20030Sstevel@tonic-gate 		mdb_printf("Searching %s...\n", c->cache_name);
20040Sstevel@tonic-gate 
20050Sstevel@tonic-gate 	w->w_cache = c;
20060Sstevel@tonic-gate 	w->w_freemem = FALSE;
20070Sstevel@tonic-gate 
20080Sstevel@tonic-gate 	if (mdb_pwalk(walk, func, w, addr) == -1) {
20090Sstevel@tonic-gate 		mdb_warn("can't find %s walker", walk);
20100Sstevel@tonic-gate 		return (WALK_DONE);
20110Sstevel@tonic-gate 	}
20120Sstevel@tonic-gate 
20130Sstevel@tonic-gate 	if (w->w_found && w->w_all == FALSE)
20140Sstevel@tonic-gate 		return (WALK_DONE);
20150Sstevel@tonic-gate 
20160Sstevel@tonic-gate 	/*
20170Sstevel@tonic-gate 	 * We have searched for allocated memory; now search for freed memory.
20180Sstevel@tonic-gate 	 */
20190Sstevel@tonic-gate 	if (w->w_verbose)
20200Sstevel@tonic-gate 		mdb_printf("Searching %s for free memory...\n", c->cache_name);
20210Sstevel@tonic-gate 
20220Sstevel@tonic-gate 	w->w_freemem = TRUE;
20230Sstevel@tonic-gate 
20240Sstevel@tonic-gate 	if (mdb_pwalk(freewalk, func, w, addr) == -1) {
20250Sstevel@tonic-gate 		mdb_warn("can't find %s walker", freewalk);
20260Sstevel@tonic-gate 		return (WALK_DONE);
20270Sstevel@tonic-gate 	}
20280Sstevel@tonic-gate 
20290Sstevel@tonic-gate 	return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT);
20300Sstevel@tonic-gate }
20310Sstevel@tonic-gate 
20320Sstevel@tonic-gate static int
20330Sstevel@tonic-gate whatis_walk_touch(uintptr_t addr, const umem_cache_t *c, whatis_t *w)
20340Sstevel@tonic-gate {
20350Sstevel@tonic-gate 	if (c->cache_cflags & UMC_NOTOUCH)
20360Sstevel@tonic-gate 		return (WALK_NEXT);
20370Sstevel@tonic-gate 
20380Sstevel@tonic-gate 	return (whatis_walk_cache(addr, c, w));
20390Sstevel@tonic-gate }
20400Sstevel@tonic-gate 
20410Sstevel@tonic-gate static int
20420Sstevel@tonic-gate whatis_walk_notouch(uintptr_t addr, const umem_cache_t *c, whatis_t *w)
20430Sstevel@tonic-gate {
20440Sstevel@tonic-gate 	if (!(c->cache_cflags & UMC_NOTOUCH))
20450Sstevel@tonic-gate 		return (WALK_NEXT);
20460Sstevel@tonic-gate 
20470Sstevel@tonic-gate 	return (whatis_walk_cache(addr, c, w));
20480Sstevel@tonic-gate }
20490Sstevel@tonic-gate 
20500Sstevel@tonic-gate int
20510Sstevel@tonic-gate whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
20520Sstevel@tonic-gate {
20530Sstevel@tonic-gate 	whatis_t w;
20540Sstevel@tonic-gate 
20550Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
20560Sstevel@tonic-gate 		return (DCMD_USAGE);
20570Sstevel@tonic-gate 
20580Sstevel@tonic-gate 	w.w_verbose = FALSE;
20590Sstevel@tonic-gate 	w.w_bufctl = FALSE;
20600Sstevel@tonic-gate 	w.w_all = FALSE;
20610Sstevel@tonic-gate 
20620Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
20630Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &w.w_verbose,
20640Sstevel@tonic-gate 	    'a', MDB_OPT_SETBITS, TRUE, &w.w_all,
20650Sstevel@tonic-gate 	    'b', MDB_OPT_SETBITS, TRUE, &w.w_bufctl, NULL) != argc)
20660Sstevel@tonic-gate 		return (DCMD_USAGE);
20670Sstevel@tonic-gate 
20680Sstevel@tonic-gate 	w.w_addr = addr;
20690Sstevel@tonic-gate 	w.w_found = 0;
20700Sstevel@tonic-gate 
20710Sstevel@tonic-gate 	/*
20720Sstevel@tonic-gate 	 * Mappings and threads should eventually be added here.
20730Sstevel@tonic-gate 	 */
20740Sstevel@tonic-gate 	if (mdb_walk("umem_cache",
20750Sstevel@tonic-gate 	    (mdb_walk_cb_t)whatis_walk_touch, &w) == -1) {
20760Sstevel@tonic-gate 		mdb_warn("couldn't find umem_cache walker");
20770Sstevel@tonic-gate 		return (DCMD_ERR);
20780Sstevel@tonic-gate 	}
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 	if (w.w_found && w.w_all == FALSE)
20810Sstevel@tonic-gate 		return (DCMD_OK);
20820Sstevel@tonic-gate 
20830Sstevel@tonic-gate 	if (mdb_walk("umem_cache",
20840Sstevel@tonic-gate 	    (mdb_walk_cb_t)whatis_walk_notouch, &w) == -1) {
20850Sstevel@tonic-gate 		mdb_warn("couldn't find umem_cache walker");
20860Sstevel@tonic-gate 		return (DCMD_ERR);
20870Sstevel@tonic-gate 	}
20880Sstevel@tonic-gate 
20890Sstevel@tonic-gate 	if (w.w_found && w.w_all == FALSE)
20900Sstevel@tonic-gate 		return (DCMD_OK);
20910Sstevel@tonic-gate 
20920Sstevel@tonic-gate 	if (mdb_walk("vmem_postfix",
20930Sstevel@tonic-gate 	    (mdb_walk_cb_t)whatis_walk_vmem, &w) == -1) {
20940Sstevel@tonic-gate 		mdb_warn("couldn't find vmem_postfix walker");
20950Sstevel@tonic-gate 		return (DCMD_ERR);
20960Sstevel@tonic-gate 	}
20970Sstevel@tonic-gate 
20980Sstevel@tonic-gate 	if (w.w_found == 0)
20990Sstevel@tonic-gate 		mdb_printf("%p is unknown\n", addr);
21000Sstevel@tonic-gate 
21010Sstevel@tonic-gate 	return (DCMD_OK);
21020Sstevel@tonic-gate }
21030Sstevel@tonic-gate 
21040Sstevel@tonic-gate typedef struct umem_log_cpu {
21050Sstevel@tonic-gate 	uintptr_t umc_low;
21060Sstevel@tonic-gate 	uintptr_t umc_high;
21070Sstevel@tonic-gate } umem_log_cpu_t;
21080Sstevel@tonic-gate 
21090Sstevel@tonic-gate int
21100Sstevel@tonic-gate umem_log_walk(uintptr_t addr, const umem_bufctl_audit_t *b, umem_log_cpu_t *umc)
21110Sstevel@tonic-gate {
21120Sstevel@tonic-gate 	int i;
21130Sstevel@tonic-gate 
21140Sstevel@tonic-gate 	for (i = 0; i < umem_max_ncpus; i++) {
21150Sstevel@tonic-gate 		if (addr >= umc[i].umc_low && addr < umc[i].umc_high)
21160Sstevel@tonic-gate 			break;
21170Sstevel@tonic-gate 	}
21180Sstevel@tonic-gate 
21190Sstevel@tonic-gate 	if (i == umem_max_ncpus)
21200Sstevel@tonic-gate 		mdb_printf("   ");
21210Sstevel@tonic-gate 	else
21220Sstevel@tonic-gate 		mdb_printf("%3d", i);
21230Sstevel@tonic-gate 
21240Sstevel@tonic-gate 	mdb_printf(" %0?p %0?p %16llx %0?p\n", addr, b->bc_addr,
21250Sstevel@tonic-gate 	    b->bc_timestamp, b->bc_thread);
21260Sstevel@tonic-gate 
21270Sstevel@tonic-gate 	return (WALK_NEXT);
21280Sstevel@tonic-gate }
21290Sstevel@tonic-gate 
21300Sstevel@tonic-gate /*ARGSUSED*/
21310Sstevel@tonic-gate int
21320Sstevel@tonic-gate umem_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
21330Sstevel@tonic-gate {
21340Sstevel@tonic-gate 	umem_log_header_t lh;
21350Sstevel@tonic-gate 	umem_cpu_log_header_t clh;
21360Sstevel@tonic-gate 	uintptr_t lhp, clhp;
21370Sstevel@tonic-gate 	umem_log_cpu_t *umc;
21380Sstevel@tonic-gate 	int i;
21390Sstevel@tonic-gate 
21400Sstevel@tonic-gate 	if (umem_readvar(&lhp, "umem_transaction_log") == -1) {
21410Sstevel@tonic-gate 		mdb_warn("failed to read 'umem_transaction_log'");
21420Sstevel@tonic-gate 		return (DCMD_ERR);
21430Sstevel@tonic-gate 	}
21440Sstevel@tonic-gate 
21450Sstevel@tonic-gate 	if (lhp == NULL) {
21460Sstevel@tonic-gate 		mdb_warn("no umem transaction log\n");
21470Sstevel@tonic-gate 		return (DCMD_ERR);
21480Sstevel@tonic-gate 	}
21490Sstevel@tonic-gate 
21500Sstevel@tonic-gate 	if (mdb_vread(&lh, sizeof (umem_log_header_t), lhp) == -1) {
21510Sstevel@tonic-gate 		mdb_warn("failed to read log header at %p", lhp);
21520Sstevel@tonic-gate 		return (DCMD_ERR);
21530Sstevel@tonic-gate 	}
21540Sstevel@tonic-gate 
21550Sstevel@tonic-gate 	clhp = lhp + ((uintptr_t)&lh.lh_cpu[0] - (uintptr_t)&lh);
21560Sstevel@tonic-gate 
21570Sstevel@tonic-gate 	umc = mdb_zalloc(sizeof (umem_log_cpu_t) * umem_max_ncpus,
21580Sstevel@tonic-gate 	    UM_SLEEP | UM_GC);
21590Sstevel@tonic-gate 
21600Sstevel@tonic-gate 	for (i = 0; i < umem_max_ncpus; i++) {
21610Sstevel@tonic-gate 		if (mdb_vread(&clh, sizeof (clh), clhp) == -1) {
21620Sstevel@tonic-gate 			mdb_warn("cannot read cpu %d's log header at %p",
21630Sstevel@tonic-gate 			    i, clhp);
21640Sstevel@tonic-gate 			return (DCMD_ERR);
21650Sstevel@tonic-gate 		}
21660Sstevel@tonic-gate 
21670Sstevel@tonic-gate 		umc[i].umc_low = clh.clh_chunk * lh.lh_chunksize +
21680Sstevel@tonic-gate 		    (uintptr_t)lh.lh_base;
21690Sstevel@tonic-gate 		umc[i].umc_high = (uintptr_t)clh.clh_current;
21700Sstevel@tonic-gate 
21710Sstevel@tonic-gate 		clhp += sizeof (umem_cpu_log_header_t);
21720Sstevel@tonic-gate 	}
21730Sstevel@tonic-gate 
21740Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags)) {
21750Sstevel@tonic-gate 		mdb_printf("%3s %-?s %-?s %16s %-?s\n", "CPU", "ADDR",
21760Sstevel@tonic-gate 		    "BUFADDR", "TIMESTAMP", "THREAD");
21770Sstevel@tonic-gate 	}
21780Sstevel@tonic-gate 
21790Sstevel@tonic-gate 	/*
21800Sstevel@tonic-gate 	 * If we have been passed an address, we'll just print out that
21810Sstevel@tonic-gate 	 * log entry.
21820Sstevel@tonic-gate 	 */
21830Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC) {
21840Sstevel@tonic-gate 		umem_bufctl_audit_t *bp;
21850Sstevel@tonic-gate 		UMEM_LOCAL_BUFCTL_AUDIT(&bp);
21860Sstevel@tonic-gate 
21870Sstevel@tonic-gate 		if (mdb_vread(bp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) {
21880Sstevel@tonic-gate 			mdb_warn("failed to read bufctl at %p", addr);
21890Sstevel@tonic-gate 			return (DCMD_ERR);
21900Sstevel@tonic-gate 		}
21910Sstevel@tonic-gate 
21920Sstevel@tonic-gate 		(void) umem_log_walk(addr, bp, umc);
21930Sstevel@tonic-gate 
21940Sstevel@tonic-gate 		return (DCMD_OK);
21950Sstevel@tonic-gate 	}
21960Sstevel@tonic-gate 
21970Sstevel@tonic-gate 	if (mdb_walk("umem_log", (mdb_walk_cb_t)umem_log_walk, umc) == -1) {
21980Sstevel@tonic-gate 		mdb_warn("can't find umem log walker");
21990Sstevel@tonic-gate 		return (DCMD_ERR);
22000Sstevel@tonic-gate 	}
22010Sstevel@tonic-gate 
22020Sstevel@tonic-gate 	return (DCMD_OK);
22030Sstevel@tonic-gate }
22040Sstevel@tonic-gate 
22050Sstevel@tonic-gate typedef struct bufctl_history_cb {
22060Sstevel@tonic-gate 	int		bhc_flags;
22070Sstevel@tonic-gate 	int		bhc_argc;
22080Sstevel@tonic-gate 	const mdb_arg_t	*bhc_argv;
22090Sstevel@tonic-gate 	int		bhc_ret;
22100Sstevel@tonic-gate } bufctl_history_cb_t;
22110Sstevel@tonic-gate 
22120Sstevel@tonic-gate /*ARGSUSED*/
22130Sstevel@tonic-gate static int
22140Sstevel@tonic-gate bufctl_history_callback(uintptr_t addr, const void *ign, void *arg)
22150Sstevel@tonic-gate {
22160Sstevel@tonic-gate 	bufctl_history_cb_t *bhc = arg;
22170Sstevel@tonic-gate 
22180Sstevel@tonic-gate 	bhc->bhc_ret =
22190Sstevel@tonic-gate 	    bufctl(addr, bhc->bhc_flags, bhc->bhc_argc, bhc->bhc_argv);
22200Sstevel@tonic-gate 
22210Sstevel@tonic-gate 	bhc->bhc_flags &= ~DCMD_LOOPFIRST;
22220Sstevel@tonic-gate 
22230Sstevel@tonic-gate 	return ((bhc->bhc_ret == DCMD_OK)? WALK_NEXT : WALK_DONE);
22240Sstevel@tonic-gate }
22250Sstevel@tonic-gate 
22260Sstevel@tonic-gate void
22270Sstevel@tonic-gate bufctl_help(void)
22280Sstevel@tonic-gate {
22290Sstevel@tonic-gate 	mdb_printf("%s\n",
22300Sstevel@tonic-gate "Display the contents of umem_bufctl_audit_ts, with optional filtering.\n");
22310Sstevel@tonic-gate 	mdb_dec_indent(2);
22320Sstevel@tonic-gate 	mdb_printf("%<b>OPTIONS%</b>\n");
22330Sstevel@tonic-gate 	mdb_inc_indent(2);
22340Sstevel@tonic-gate 	mdb_printf("%s",
22350Sstevel@tonic-gate "  -v    Display the full content of the bufctl, including its stack trace\n"
22360Sstevel@tonic-gate "  -h    retrieve the bufctl's transaction history, if available\n"
22370Sstevel@tonic-gate "  -a addr\n"
22380Sstevel@tonic-gate "        filter out bufctls not involving the buffer at addr\n"
22390Sstevel@tonic-gate "  -c caller\n"
22400Sstevel@tonic-gate "        filter out bufctls without the function/PC in their stack trace\n"
22410Sstevel@tonic-gate "  -e earliest\n"
22420Sstevel@tonic-gate "        filter out bufctls timestamped before earliest\n"
22430Sstevel@tonic-gate "  -l latest\n"
22440Sstevel@tonic-gate "        filter out bufctls timestamped after latest\n"
22450Sstevel@tonic-gate "  -t thread\n"
22460Sstevel@tonic-gate "        filter out bufctls not involving thread\n");
22470Sstevel@tonic-gate }
22480Sstevel@tonic-gate 
22490Sstevel@tonic-gate int
22500Sstevel@tonic-gate bufctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
22510Sstevel@tonic-gate {
22520Sstevel@tonic-gate 	uint_t verbose = FALSE;
22530Sstevel@tonic-gate 	uint_t history = FALSE;
22540Sstevel@tonic-gate 	uint_t in_history = FALSE;
22550Sstevel@tonic-gate 	uintptr_t caller = NULL, thread = NULL;
22560Sstevel@tonic-gate 	uintptr_t laddr, haddr, baddr = NULL;
22570Sstevel@tonic-gate 	hrtime_t earliest = 0, latest = 0;
22580Sstevel@tonic-gate 	int i, depth;
22590Sstevel@tonic-gate 	char c[MDB_SYM_NAMLEN];
22600Sstevel@tonic-gate 	GElf_Sym sym;
22610Sstevel@tonic-gate 	umem_bufctl_audit_t *bcp;
22620Sstevel@tonic-gate 	UMEM_LOCAL_BUFCTL_AUDIT(&bcp);
22630Sstevel@tonic-gate 
22640Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
22650Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
22660Sstevel@tonic-gate 	    'h', MDB_OPT_SETBITS, TRUE, &history,
22670Sstevel@tonic-gate 	    'H', MDB_OPT_SETBITS, TRUE, &in_history,		/* internal */
22680Sstevel@tonic-gate 	    'c', MDB_OPT_UINTPTR, &caller,
22690Sstevel@tonic-gate 	    't', MDB_OPT_UINTPTR, &thread,
22700Sstevel@tonic-gate 	    'e', MDB_OPT_UINT64, &earliest,
22710Sstevel@tonic-gate 	    'l', MDB_OPT_UINT64, &latest,
22720Sstevel@tonic-gate 	    'a', MDB_OPT_UINTPTR, &baddr, NULL) != argc)
22730Sstevel@tonic-gate 		return (DCMD_USAGE);
22740Sstevel@tonic-gate 
22750Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
22760Sstevel@tonic-gate 		return (DCMD_USAGE);
22770Sstevel@tonic-gate 
22780Sstevel@tonic-gate 	if (in_history && !history)
22790Sstevel@tonic-gate 		return (DCMD_USAGE);
22800Sstevel@tonic-gate 
22810Sstevel@tonic-gate 	if (history && !in_history) {
22820Sstevel@tonic-gate 		mdb_arg_t *nargv = mdb_zalloc(sizeof (*nargv) * (argc + 1),
22830Sstevel@tonic-gate 		    UM_SLEEP | UM_GC);
22840Sstevel@tonic-gate 		bufctl_history_cb_t bhc;
22850Sstevel@tonic-gate 
22860Sstevel@tonic-gate 		nargv[0].a_type = MDB_TYPE_STRING;
22870Sstevel@tonic-gate 		nargv[0].a_un.a_str = "-H";		/* prevent recursion */
22880Sstevel@tonic-gate 
22890Sstevel@tonic-gate 		for (i = 0; i < argc; i++)
22900Sstevel@tonic-gate 			nargv[i + 1] = argv[i];
22910Sstevel@tonic-gate 
22920Sstevel@tonic-gate 		/*
22930Sstevel@tonic-gate 		 * When in history mode, we treat each element as if it
22940Sstevel@tonic-gate 		 * were in a seperate loop, so that the headers group
22950Sstevel@tonic-gate 		 * bufctls with similar histories.
22960Sstevel@tonic-gate 		 */
22970Sstevel@tonic-gate 		bhc.bhc_flags = flags | DCMD_LOOP | DCMD_LOOPFIRST;
22980Sstevel@tonic-gate 		bhc.bhc_argc = argc + 1;
22990Sstevel@tonic-gate 		bhc.bhc_argv = nargv;
23000Sstevel@tonic-gate 		bhc.bhc_ret = DCMD_OK;
23010Sstevel@tonic-gate 
23020Sstevel@tonic-gate 		if (mdb_pwalk("bufctl_history", bufctl_history_callback, &bhc,
23030Sstevel@tonic-gate 		    addr) == -1) {
23040Sstevel@tonic-gate 			mdb_warn("unable to walk bufctl_history");
23050Sstevel@tonic-gate 			return (DCMD_ERR);
23060Sstevel@tonic-gate 		}
23070Sstevel@tonic-gate 
23080Sstevel@tonic-gate 		if (bhc.bhc_ret == DCMD_OK && !(flags & DCMD_PIPE_OUT))
23090Sstevel@tonic-gate 			mdb_printf("\n");
23100Sstevel@tonic-gate 
23110Sstevel@tonic-gate 		return (bhc.bhc_ret);
23120Sstevel@tonic-gate 	}
23130Sstevel@tonic-gate 
23140Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
23150Sstevel@tonic-gate 		if (verbose) {
23160Sstevel@tonic-gate 			mdb_printf("%16s %16s %16s %16s\n"
23170Sstevel@tonic-gate 			    "%<u>%16s %16s %16s %16s%</u>\n",
23180Sstevel@tonic-gate 			    "ADDR", "BUFADDR", "TIMESTAMP", "THREAD",
23190Sstevel@tonic-gate 			    "", "CACHE", "LASTLOG", "CONTENTS");
23200Sstevel@tonic-gate 		} else {
23210Sstevel@tonic-gate 			mdb_printf("%<u>%-?s %-?s %-12s %5s %s%</u>\n",
23220Sstevel@tonic-gate 			    "ADDR", "BUFADDR", "TIMESTAMP", "THRD", "CALLER");
23230Sstevel@tonic-gate 		}
23240Sstevel@tonic-gate 	}
23250Sstevel@tonic-gate 
23260Sstevel@tonic-gate 	if (mdb_vread(bcp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) {
23270Sstevel@tonic-gate 		mdb_warn("couldn't read bufctl at %p", addr);
23280Sstevel@tonic-gate 		return (DCMD_ERR);
23290Sstevel@tonic-gate 	}
23300Sstevel@tonic-gate 
23310Sstevel@tonic-gate 	/*
23320Sstevel@tonic-gate 	 * Guard against bogus bc_depth in case the bufctl is corrupt or
23330Sstevel@tonic-gate 	 * the address does not really refer to a bufctl.
23340Sstevel@tonic-gate 	 */
23350Sstevel@tonic-gate 	depth = MIN(bcp->bc_depth, umem_stack_depth);
23360Sstevel@tonic-gate 
23370Sstevel@tonic-gate 	if (caller != NULL) {
23380Sstevel@tonic-gate 		laddr = caller;
23390Sstevel@tonic-gate 		haddr = caller + sizeof (caller);
23400Sstevel@tonic-gate 
23410Sstevel@tonic-gate 		if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c, sizeof (c),
23420Sstevel@tonic-gate 		    &sym) != -1 && caller == (uintptr_t)sym.st_value) {
23430Sstevel@tonic-gate 			/*
23440Sstevel@tonic-gate 			 * We were provided an exact symbol value; any
23450Sstevel@tonic-gate 			 * address in the function is valid.
23460Sstevel@tonic-gate 			 */
23470Sstevel@tonic-gate 			laddr = (uintptr_t)sym.st_value;
23480Sstevel@tonic-gate 			haddr = (uintptr_t)sym.st_value + sym.st_size;
23490Sstevel@tonic-gate 		}
23500Sstevel@tonic-gate 
23510Sstevel@tonic-gate 		for (i = 0; i < depth; i++)
23520Sstevel@tonic-gate 			if (bcp->bc_stack[i] >= laddr &&
23530Sstevel@tonic-gate 			    bcp->bc_stack[i] < haddr)
23540Sstevel@tonic-gate 				break;
23550Sstevel@tonic-gate 
23560Sstevel@tonic-gate 		if (i == depth)
23570Sstevel@tonic-gate 			return (DCMD_OK);
23580Sstevel@tonic-gate 	}
23590Sstevel@tonic-gate 
23600Sstevel@tonic-gate 	if (thread != NULL && (uintptr_t)bcp->bc_thread != thread)
23610Sstevel@tonic-gate 		return (DCMD_OK);
23620Sstevel@tonic-gate 
23630Sstevel@tonic-gate 	if (earliest != 0 && bcp->bc_timestamp < earliest)
23640Sstevel@tonic-gate 		return (DCMD_OK);
23650Sstevel@tonic-gate 
23660Sstevel@tonic-gate 	if (latest != 0 && bcp->bc_timestamp > latest)
23670Sstevel@tonic-gate 		return (DCMD_OK);
23680Sstevel@tonic-gate 
23690Sstevel@tonic-gate 	if (baddr != 0 && (uintptr_t)bcp->bc_addr != baddr)
23700Sstevel@tonic-gate 		return (DCMD_OK);
23710Sstevel@tonic-gate 
23720Sstevel@tonic-gate 	if (flags & DCMD_PIPE_OUT) {
23730Sstevel@tonic-gate 		mdb_printf("%#r\n", addr);
23740Sstevel@tonic-gate 		return (DCMD_OK);
23750Sstevel@tonic-gate 	}
23760Sstevel@tonic-gate 
23770Sstevel@tonic-gate 	if (verbose) {
23780Sstevel@tonic-gate 		mdb_printf(
23790Sstevel@tonic-gate 		    "%<b>%16p%</b> %16p %16llx %16d\n"
23800Sstevel@tonic-gate 		    "%16s %16p %16p %16p\n",
23810Sstevel@tonic-gate 		    addr, bcp->bc_addr, bcp->bc_timestamp, bcp->bc_thread,
23820Sstevel@tonic-gate 		    "", bcp->bc_cache, bcp->bc_lastlog, bcp->bc_contents);
23830Sstevel@tonic-gate 
23840Sstevel@tonic-gate 		mdb_inc_indent(17);
23850Sstevel@tonic-gate 		for (i = 0; i < depth; i++)
23860Sstevel@tonic-gate 			mdb_printf("%a\n", bcp->bc_stack[i]);
23870Sstevel@tonic-gate 		mdb_dec_indent(17);
23880Sstevel@tonic-gate 		mdb_printf("\n");
23890Sstevel@tonic-gate 	} else {
23900Sstevel@tonic-gate 		mdb_printf("%0?p %0?p %12llx %5d", addr, bcp->bc_addr,
23910Sstevel@tonic-gate 		    bcp->bc_timestamp, bcp->bc_thread);
23920Sstevel@tonic-gate 
23930Sstevel@tonic-gate 		for (i = 0; i < depth; i++) {
23940Sstevel@tonic-gate 			if (mdb_lookup_by_addr(bcp->bc_stack[i],
23950Sstevel@tonic-gate 			    MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1)
23960Sstevel@tonic-gate 				continue;
23970Sstevel@tonic-gate 			if (is_umem_sym(c, "umem_"))
23980Sstevel@tonic-gate 				continue;
23990Sstevel@tonic-gate 			mdb_printf(" %a\n", bcp->bc_stack[i]);
24000Sstevel@tonic-gate 			break;
24010Sstevel@tonic-gate 		}
24020Sstevel@tonic-gate 
24030Sstevel@tonic-gate 		if (i >= depth)
24040Sstevel@tonic-gate 			mdb_printf("\n");
24050Sstevel@tonic-gate 	}
24060Sstevel@tonic-gate 
24070Sstevel@tonic-gate 	return (DCMD_OK);
24080Sstevel@tonic-gate }
24090Sstevel@tonic-gate 
24100Sstevel@tonic-gate /*ARGSUSED*/
24110Sstevel@tonic-gate int
24120Sstevel@tonic-gate bufctl_audit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
24130Sstevel@tonic-gate {
24140Sstevel@tonic-gate 	mdb_arg_t a;
24150Sstevel@tonic-gate 
24160Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
24170Sstevel@tonic-gate 		return (DCMD_USAGE);
24180Sstevel@tonic-gate 
24190Sstevel@tonic-gate 	if (argc != 0)
24200Sstevel@tonic-gate 		return (DCMD_USAGE);
24210Sstevel@tonic-gate 
24220Sstevel@tonic-gate 	a.a_type = MDB_TYPE_STRING;
24230Sstevel@tonic-gate 	a.a_un.a_str = "-v";
24240Sstevel@tonic-gate 
24250Sstevel@tonic-gate 	return (bufctl(addr, flags, 1, &a));
24260Sstevel@tonic-gate }
24270Sstevel@tonic-gate 
24280Sstevel@tonic-gate typedef struct umem_verify {
24290Sstevel@tonic-gate 	uint64_t *umv_buf;		/* buffer to read cache contents into */
24300Sstevel@tonic-gate 	size_t umv_size;		/* number of bytes in umv_buf */
24310Sstevel@tonic-gate 	int umv_corruption;		/* > 0 if corruption found. */
24320Sstevel@tonic-gate 	int umv_besilent;		/* report actual corruption sites */
24330Sstevel@tonic-gate 	struct umem_cache umv_cache;	/* the cache we're operating on */
24340Sstevel@tonic-gate } umem_verify_t;
24350Sstevel@tonic-gate 
24360Sstevel@tonic-gate /*
24370Sstevel@tonic-gate  * verify_pattern()
24380Sstevel@tonic-gate  *	verify that buf is filled with the pattern pat.
24390Sstevel@tonic-gate  */
24400Sstevel@tonic-gate static int64_t
24410Sstevel@tonic-gate verify_pattern(uint64_t *buf_arg, size_t size, uint64_t pat)
24420Sstevel@tonic-gate {
24430Sstevel@tonic-gate 	/*LINTED*/
24440Sstevel@tonic-gate 	uint64_t *bufend = (uint64_t *)((char *)buf_arg + size);
24450Sstevel@tonic-gate 	uint64_t *buf;
24460Sstevel@tonic-gate 
24470Sstevel@tonic-gate 	for (buf = buf_arg; buf < bufend; buf++)
24480Sstevel@tonic-gate 		if (*buf != pat)
24490Sstevel@tonic-gate 			return ((uintptr_t)buf - (uintptr_t)buf_arg);
24500Sstevel@tonic-gate 	return (-1);
24510Sstevel@tonic-gate }
24520Sstevel@tonic-gate 
24530Sstevel@tonic-gate /*
24540Sstevel@tonic-gate  * verify_buftag()
24550Sstevel@tonic-gate  *	verify that btp->bt_bxstat == (bcp ^ pat)
24560Sstevel@tonic-gate  */
24570Sstevel@tonic-gate static int
24580Sstevel@tonic-gate verify_buftag(umem_buftag_t *btp, uintptr_t pat)
24590Sstevel@tonic-gate {
24600Sstevel@tonic-gate 	return (btp->bt_bxstat == ((intptr_t)btp->bt_bufctl ^ pat) ? 0 : -1);
24610Sstevel@tonic-gate }
24620Sstevel@tonic-gate 
24630Sstevel@tonic-gate /*
24640Sstevel@tonic-gate  * verify_free()
24650Sstevel@tonic-gate  *	verify the integrity of a free block of memory by checking
24660Sstevel@tonic-gate  *	that it is filled with 0xdeadbeef and that its buftag is sane.
24670Sstevel@tonic-gate  */
24680Sstevel@tonic-gate /*ARGSUSED1*/
24690Sstevel@tonic-gate static int
24700Sstevel@tonic-gate verify_free(uintptr_t addr, const void *data, void *private)
24710Sstevel@tonic-gate {
24720Sstevel@tonic-gate 	umem_verify_t *umv = (umem_verify_t *)private;
24730Sstevel@tonic-gate 	uint64_t *buf = umv->umv_buf;	/* buf to validate */
24740Sstevel@tonic-gate 	int64_t corrupt;		/* corruption offset */
24750Sstevel@tonic-gate 	umem_buftag_t *buftagp;		/* ptr to buftag */
24760Sstevel@tonic-gate 	umem_cache_t *cp = &umv->umv_cache;
24770Sstevel@tonic-gate 	int besilent = umv->umv_besilent;
24780Sstevel@tonic-gate 
24790Sstevel@tonic-gate 	/*LINTED*/
24800Sstevel@tonic-gate 	buftagp = UMEM_BUFTAG(cp, buf);
24810Sstevel@tonic-gate 
24820Sstevel@tonic-gate 	/*
24830Sstevel@tonic-gate 	 * Read the buffer to check.
24840Sstevel@tonic-gate 	 */
24850Sstevel@tonic-gate 	if (mdb_vread(buf, umv->umv_size, addr) == -1) {
24860Sstevel@tonic-gate 		if (!besilent)
24870Sstevel@tonic-gate 			mdb_warn("couldn't read %p", addr);
24880Sstevel@tonic-gate 		return (WALK_NEXT);
24890Sstevel@tonic-gate 	}
24900Sstevel@tonic-gate 
24910Sstevel@tonic-gate 	if ((corrupt = verify_pattern(buf, cp->cache_verify,
24920Sstevel@tonic-gate 	    UMEM_FREE_PATTERN)) >= 0) {
24930Sstevel@tonic-gate 		if (!besilent)
24940Sstevel@tonic-gate 			mdb_printf("buffer %p (free) seems corrupted, at %p\n",
24950Sstevel@tonic-gate 			    addr, (uintptr_t)addr + corrupt);
24960Sstevel@tonic-gate 		goto corrupt;
24970Sstevel@tonic-gate 	}
24980Sstevel@tonic-gate 
24990Sstevel@tonic-gate 	if ((cp->cache_flags & UMF_HASH) &&
25000Sstevel@tonic-gate 	    buftagp->bt_redzone != UMEM_REDZONE_PATTERN) {
25010Sstevel@tonic-gate 		if (!besilent)
25020Sstevel@tonic-gate 			mdb_printf("buffer %p (free) seems to "
25030Sstevel@tonic-gate 			    "have a corrupt redzone pattern\n", addr);
25040Sstevel@tonic-gate 		goto corrupt;
25050Sstevel@tonic-gate 	}
25060Sstevel@tonic-gate 
25070Sstevel@tonic-gate 	/*
25080Sstevel@tonic-gate 	 * confirm bufctl pointer integrity.
25090Sstevel@tonic-gate 	 */
25100Sstevel@tonic-gate 	if (verify_buftag(buftagp, UMEM_BUFTAG_FREE) == -1) {
25110Sstevel@tonic-gate 		if (!besilent)
25120Sstevel@tonic-gate 			mdb_printf("buffer %p (free) has a corrupt "
25130Sstevel@tonic-gate 			    "buftag\n", addr);
25140Sstevel@tonic-gate 		goto corrupt;
25150Sstevel@tonic-gate 	}
25160Sstevel@tonic-gate 
25170Sstevel@tonic-gate 	return (WALK_NEXT);
25180Sstevel@tonic-gate corrupt:
25190Sstevel@tonic-gate 	umv->umv_corruption++;
25200Sstevel@tonic-gate 	return (WALK_NEXT);
25210Sstevel@tonic-gate }
25220Sstevel@tonic-gate 
25230Sstevel@tonic-gate /*
25240Sstevel@tonic-gate  * verify_alloc()
25250Sstevel@tonic-gate  *	Verify that the buftag of an allocated buffer makes sense with respect
25260Sstevel@tonic-gate  *	to the buffer.
25270Sstevel@tonic-gate  */
25280Sstevel@tonic-gate /*ARGSUSED1*/
25290Sstevel@tonic-gate static int
25300Sstevel@tonic-gate verify_alloc(uintptr_t addr, const void *data, void *private)
25310Sstevel@tonic-gate {
25320Sstevel@tonic-gate 	umem_verify_t *umv = (umem_verify_t *)private;
25330Sstevel@tonic-gate 	umem_cache_t *cp = &umv->umv_cache;
25340Sstevel@tonic-gate 	uint64_t *buf = umv->umv_buf;	/* buf to validate */
25350Sstevel@tonic-gate 	/*LINTED*/
25360Sstevel@tonic-gate 	umem_buftag_t *buftagp = UMEM_BUFTAG(cp, buf);
25370Sstevel@tonic-gate 	uint32_t *ip = (uint32_t *)buftagp;
25380Sstevel@tonic-gate 	uint8_t *bp = (uint8_t *)buf;
25390Sstevel@tonic-gate 	int looks_ok = 0, size_ok = 1;	/* flags for finding corruption */
25400Sstevel@tonic-gate 	int besilent = umv->umv_besilent;
25410Sstevel@tonic-gate 
25420Sstevel@tonic-gate 	/*
25430Sstevel@tonic-gate 	 * Read the buffer to check.
25440Sstevel@tonic-gate 	 */
25450Sstevel@tonic-gate 	if (mdb_vread(buf, umv->umv_size, addr) == -1) {
25460Sstevel@tonic-gate 		if (!besilent)
25470Sstevel@tonic-gate 			mdb_warn("couldn't read %p", addr);
25480Sstevel@tonic-gate 		return (WALK_NEXT);
25490Sstevel@tonic-gate 	}
25500Sstevel@tonic-gate 
25510Sstevel@tonic-gate 	/*
25520Sstevel@tonic-gate 	 * There are two cases to handle:
25530Sstevel@tonic-gate 	 * 1. If the buf was alloc'd using umem_cache_alloc, it will have
25540Sstevel@tonic-gate 	 *    0xfeedfacefeedface at the end of it
25550Sstevel@tonic-gate 	 * 2. If the buf was alloc'd using umem_alloc, it will have
25560Sstevel@tonic-gate 	 *    0xbb just past the end of the region in use.  At the buftag,
25570Sstevel@tonic-gate 	 *    it will have 0xfeedface (or, if the whole buffer is in use,
25580Sstevel@tonic-gate 	 *    0xfeedface & bb000000 or 0xfeedfacf & 000000bb depending on
25590Sstevel@tonic-gate 	 *    endianness), followed by 32 bits containing the offset of the
25600Sstevel@tonic-gate 	 *    0xbb byte in the buffer.
25610Sstevel@tonic-gate 	 *
25620Sstevel@tonic-gate 	 * Finally, the two 32-bit words that comprise the second half of the
25630Sstevel@tonic-gate 	 * buftag should xor to UMEM_BUFTAG_ALLOC
25640Sstevel@tonic-gate 	 */
25650Sstevel@tonic-gate 
25660Sstevel@tonic-gate 	if (buftagp->bt_redzone == UMEM_REDZONE_PATTERN)
25670Sstevel@tonic-gate 		looks_ok = 1;
25680Sstevel@tonic-gate 	else if (!UMEM_SIZE_VALID(ip[1]))
25690Sstevel@tonic-gate 		size_ok = 0;
25700Sstevel@tonic-gate 	else if (bp[UMEM_SIZE_DECODE(ip[1])] == UMEM_REDZONE_BYTE)
25710Sstevel@tonic-gate 		looks_ok = 1;
25720Sstevel@tonic-gate 	else
25730Sstevel@tonic-gate 		size_ok = 0;
25740Sstevel@tonic-gate 
25750Sstevel@tonic-gate 	if (!size_ok) {
25760Sstevel@tonic-gate 		if (!besilent)
25770Sstevel@tonic-gate 			mdb_printf("buffer %p (allocated) has a corrupt "
25780Sstevel@tonic-gate 			    "redzone size encoding\n", addr);
25790Sstevel@tonic-gate 		goto corrupt;
25800Sstevel@tonic-gate 	}
25810Sstevel@tonic-gate 
25820Sstevel@tonic-gate 	if (!looks_ok) {
25830Sstevel@tonic-gate 		if (!besilent)
25840Sstevel@tonic-gate 			mdb_printf("buffer %p (allocated) has a corrupt "
25850Sstevel@tonic-gate 			    "redzone signature\n", addr);
25860Sstevel@tonic-gate 		goto corrupt;
25870Sstevel@tonic-gate 	}
25880Sstevel@tonic-gate 
25890Sstevel@tonic-gate 	if (verify_buftag(buftagp, UMEM_BUFTAG_ALLOC) == -1) {
25900Sstevel@tonic-gate 		if (!besilent)
25910Sstevel@tonic-gate 			mdb_printf("buffer %p (allocated) has a "
25920Sstevel@tonic-gate 			    "corrupt buftag\n", addr);
25930Sstevel@tonic-gate 		goto corrupt;
25940Sstevel@tonic-gate 	}
25950Sstevel@tonic-gate 
25960Sstevel@tonic-gate 	return (WALK_NEXT);
25970Sstevel@tonic-gate corrupt:
25980Sstevel@tonic-gate 	umv->umv_corruption++;
25990Sstevel@tonic-gate 	return (WALK_NEXT);
26000Sstevel@tonic-gate }
26010Sstevel@tonic-gate 
26020Sstevel@tonic-gate /*ARGSUSED2*/
26030Sstevel@tonic-gate int
26040Sstevel@tonic-gate umem_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
26050Sstevel@tonic-gate {
26060Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC) {
26070Sstevel@tonic-gate 		int check_alloc = 0, check_free = 0;
26080Sstevel@tonic-gate 		umem_verify_t umv;
26090Sstevel@tonic-gate 
26100Sstevel@tonic-gate 		if (mdb_vread(&umv.umv_cache, sizeof (umv.umv_cache),
26110Sstevel@tonic-gate 		    addr) == -1) {
26120Sstevel@tonic-gate 			mdb_warn("couldn't read umem_cache %p", addr);
26130Sstevel@tonic-gate 			return (DCMD_ERR);
26140Sstevel@tonic-gate 		}
26150Sstevel@tonic-gate 
26160Sstevel@tonic-gate 		umv.umv_size = umv.umv_cache.cache_buftag +
26170Sstevel@tonic-gate 		    sizeof (umem_buftag_t);
26180Sstevel@tonic-gate 		umv.umv_buf = mdb_alloc(umv.umv_size, UM_SLEEP | UM_GC);
26190Sstevel@tonic-gate 		umv.umv_corruption = 0;
26200Sstevel@tonic-gate 
26210Sstevel@tonic-gate 		if ((umv.umv_cache.cache_flags & UMF_REDZONE)) {
26220Sstevel@tonic-gate 			check_alloc = 1;
26230Sstevel@tonic-gate 			if (umv.umv_cache.cache_flags & UMF_DEADBEEF)
26240Sstevel@tonic-gate 				check_free = 1;
26250Sstevel@tonic-gate 		} else {
26260Sstevel@tonic-gate 			if (!(flags & DCMD_LOOP)) {
26270Sstevel@tonic-gate 				mdb_warn("cache %p (%s) does not have "
26280Sstevel@tonic-gate 				    "redzone checking enabled\n", addr,
26290Sstevel@tonic-gate 				    umv.umv_cache.cache_name);
26300Sstevel@tonic-gate 			}
26310Sstevel@tonic-gate 			return (DCMD_ERR);
26320Sstevel@tonic-gate 		}
26330Sstevel@tonic-gate 
26340Sstevel@tonic-gate 		if (flags & DCMD_LOOP) {
26350Sstevel@tonic-gate 			/*
26360Sstevel@tonic-gate 			 * table mode, don't print out every corrupt buffer
26370Sstevel@tonic-gate 			 */
26380Sstevel@tonic-gate 			umv.umv_besilent = 1;
26390Sstevel@tonic-gate 		} else {
26400Sstevel@tonic-gate 			mdb_printf("Summary for cache '%s'\n",
26410Sstevel@tonic-gate 			    umv.umv_cache.cache_name);
26420Sstevel@tonic-gate 			mdb_inc_indent(2);
26430Sstevel@tonic-gate 			umv.umv_besilent = 0;
26440Sstevel@tonic-gate 		}
26450Sstevel@tonic-gate 
26460Sstevel@tonic-gate 		if (check_alloc)
26470Sstevel@tonic-gate 			(void) mdb_pwalk("umem", verify_alloc, &umv, addr);
26480Sstevel@tonic-gate 		if (check_free)
26490Sstevel@tonic-gate 			(void) mdb_pwalk("freemem", verify_free, &umv, addr);
26500Sstevel@tonic-gate 
26510Sstevel@tonic-gate 		if (flags & DCMD_LOOP) {
26520Sstevel@tonic-gate 			if (umv.umv_corruption == 0) {
26530Sstevel@tonic-gate 				mdb_printf("%-*s %?p clean\n",
26540Sstevel@tonic-gate 				    UMEM_CACHE_NAMELEN,
26550Sstevel@tonic-gate 				    umv.umv_cache.cache_name, addr);
26560Sstevel@tonic-gate 			} else {
26570Sstevel@tonic-gate 				char *s = "";	/* optional s in "buffer[s]" */
26580Sstevel@tonic-gate 				if (umv.umv_corruption > 1)
26590Sstevel@tonic-gate 					s = "s";
26600Sstevel@tonic-gate 
26610Sstevel@tonic-gate 				mdb_printf("%-*s %?p %d corrupt buffer%s\n",
26620Sstevel@tonic-gate 				    UMEM_CACHE_NAMELEN,
26630Sstevel@tonic-gate 				    umv.umv_cache.cache_name, addr,
26640Sstevel@tonic-gate 				    umv.umv_corruption, s);
26650Sstevel@tonic-gate 			}
26660Sstevel@tonic-gate 		} else {
26670Sstevel@tonic-gate 			/*
26680Sstevel@tonic-gate 			 * This is the more verbose mode, when the user has
26690Sstevel@tonic-gate 			 * type addr::umem_verify.  If the cache was clean,
26700Sstevel@tonic-gate 			 * nothing will have yet been printed. So say something.
26710Sstevel@tonic-gate 			 */
26720Sstevel@tonic-gate 			if (umv.umv_corruption == 0)
26730Sstevel@tonic-gate 				mdb_printf("clean\n");
26740Sstevel@tonic-gate 
26750Sstevel@tonic-gate 			mdb_dec_indent(2);
26760Sstevel@tonic-gate 		}
26770Sstevel@tonic-gate 	} else {
26780Sstevel@tonic-gate 		/*
26790Sstevel@tonic-gate 		 * If the user didn't specify a cache to verify, we'll walk all
26800Sstevel@tonic-gate 		 * umem_cache's, specifying ourself as a callback for each...
26810Sstevel@tonic-gate 		 * this is the equivalent of '::walk umem_cache .::umem_verify'
26820Sstevel@tonic-gate 		 */
26830Sstevel@tonic-gate 		mdb_printf("%<u>%-*s %-?s %-20s%</b>\n", UMEM_CACHE_NAMELEN,
26840Sstevel@tonic-gate 		    "Cache Name", "Addr", "Cache Integrity");
26850Sstevel@tonic-gate 		(void) (mdb_walk_dcmd("umem_cache", "umem_verify", 0, NULL));
26860Sstevel@tonic-gate 	}
26870Sstevel@tonic-gate 
26880Sstevel@tonic-gate 	return (DCMD_OK);
26890Sstevel@tonic-gate }
26900Sstevel@tonic-gate 
26910Sstevel@tonic-gate typedef struct vmem_node {
26920Sstevel@tonic-gate 	struct vmem_node *vn_next;
26930Sstevel@tonic-gate 	struct vmem_node *vn_parent;
26940Sstevel@tonic-gate 	struct vmem_node *vn_sibling;
26950Sstevel@tonic-gate 	struct vmem_node *vn_children;
26960Sstevel@tonic-gate 	uintptr_t vn_addr;
26970Sstevel@tonic-gate 	int vn_marked;
26980Sstevel@tonic-gate 	vmem_t vn_vmem;
26990Sstevel@tonic-gate } vmem_node_t;
27000Sstevel@tonic-gate 
27010Sstevel@tonic-gate typedef struct vmem_walk {
27020Sstevel@tonic-gate 	vmem_node_t *vw_root;
27030Sstevel@tonic-gate 	vmem_node_t *vw_current;
27040Sstevel@tonic-gate } vmem_walk_t;
27050Sstevel@tonic-gate 
27060Sstevel@tonic-gate int
27070Sstevel@tonic-gate vmem_walk_init(mdb_walk_state_t *wsp)
27080Sstevel@tonic-gate {
27090Sstevel@tonic-gate 	uintptr_t vaddr, paddr;
27100Sstevel@tonic-gate 	vmem_node_t *head = NULL, *root = NULL, *current = NULL, *parent, *vp;
27110Sstevel@tonic-gate 	vmem_walk_t *vw;
27120Sstevel@tonic-gate 
27130Sstevel@tonic-gate 	if (umem_readvar(&vaddr, "vmem_list") == -1) {
27140Sstevel@tonic-gate 		mdb_warn("couldn't read 'vmem_list'");
27150Sstevel@tonic-gate 		return (WALK_ERR);
27160Sstevel@tonic-gate 	}
27170Sstevel@tonic-gate 
27180Sstevel@tonic-gate 	while (vaddr != NULL) {
27190Sstevel@tonic-gate 		vp = mdb_zalloc(sizeof (vmem_node_t), UM_SLEEP);
27200Sstevel@tonic-gate 		vp->vn_addr = vaddr;
27210Sstevel@tonic-gate 		vp->vn_next = head;
27220Sstevel@tonic-gate 		head = vp;
27230Sstevel@tonic-gate 
27240Sstevel@tonic-gate 		if (vaddr == wsp->walk_addr)
27250Sstevel@tonic-gate 			current = vp;
27260Sstevel@tonic-gate 
27270Sstevel@tonic-gate 		if (mdb_vread(&vp->vn_vmem, sizeof (vmem_t), vaddr) == -1) {
27280Sstevel@tonic-gate 			mdb_warn("couldn't read vmem_t at %p", vaddr);
27290Sstevel@tonic-gate 			goto err;
27300Sstevel@tonic-gate 		}
27310Sstevel@tonic-gate 
27320Sstevel@tonic-gate 		vaddr = (uintptr_t)vp->vn_vmem.vm_next;
27330Sstevel@tonic-gate 	}
27340Sstevel@tonic-gate 
27350Sstevel@tonic-gate 	for (vp = head; vp != NULL; vp = vp->vn_next) {
27360Sstevel@tonic-gate 
27370Sstevel@tonic-gate 		if ((paddr = (uintptr_t)vp->vn_vmem.vm_source) == NULL) {
27380Sstevel@tonic-gate 			vp->vn_sibling = root;
27390Sstevel@tonic-gate 			root = vp;
27400Sstevel@tonic-gate 			continue;
27410Sstevel@tonic-gate 		}
27420Sstevel@tonic-gate 
27430Sstevel@tonic-gate 		for (parent = head; parent != NULL; parent = parent->vn_next) {
27440Sstevel@tonic-gate 			if (parent->vn_addr != paddr)
27450Sstevel@tonic-gate 				continue;
27460Sstevel@tonic-gate 			vp->vn_sibling = parent->vn_children;
27470Sstevel@tonic-gate 			parent->vn_children = vp;
27480Sstevel@tonic-gate 			vp->vn_parent = parent;
27490Sstevel@tonic-gate 			break;
27500Sstevel@tonic-gate 		}
27510Sstevel@tonic-gate 
27520Sstevel@tonic-gate 		if (parent == NULL) {
27530Sstevel@tonic-gate 			mdb_warn("couldn't find %p's parent (%p)\n",
27540Sstevel@tonic-gate 			    vp->vn_addr, paddr);
27550Sstevel@tonic-gate 			goto err;
27560Sstevel@tonic-gate 		}
27570Sstevel@tonic-gate 	}
27580Sstevel@tonic-gate 
27590Sstevel@tonic-gate 	vw = mdb_zalloc(sizeof (vmem_walk_t), UM_SLEEP);
27600Sstevel@tonic-gate 	vw->vw_root = root;
27610Sstevel@tonic-gate 
27620Sstevel@tonic-gate 	if (current != NULL)
27630Sstevel@tonic-gate 		vw->vw_current = current;
27640Sstevel@tonic-gate 	else
27650Sstevel@tonic-gate 		vw->vw_current = root;
27660Sstevel@tonic-gate 
27670Sstevel@tonic-gate 	wsp->walk_data = vw;
27680Sstevel@tonic-gate 	return (WALK_NEXT);
27690Sstevel@tonic-gate err:
27700Sstevel@tonic-gate 	for (vp = head; head != NULL; vp = head) {
27710Sstevel@tonic-gate 		head = vp->vn_next;
27720Sstevel@tonic-gate 		mdb_free(vp, sizeof (vmem_node_t));
27730Sstevel@tonic-gate 	}
27740Sstevel@tonic-gate 
27750Sstevel@tonic-gate 	return (WALK_ERR);
27760Sstevel@tonic-gate }
27770Sstevel@tonic-gate 
27780Sstevel@tonic-gate int
27790Sstevel@tonic-gate vmem_walk_step(mdb_walk_state_t *wsp)
27800Sstevel@tonic-gate {
27810Sstevel@tonic-gate 	vmem_walk_t *vw = wsp->walk_data;
27820Sstevel@tonic-gate 	vmem_node_t *vp;
27830Sstevel@tonic-gate 	int rval;
27840Sstevel@tonic-gate 
27850Sstevel@tonic-gate 	if ((vp = vw->vw_current) == NULL)
27860Sstevel@tonic-gate 		return (WALK_DONE);
27870Sstevel@tonic-gate 
27880Sstevel@tonic-gate 	rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata);
27890Sstevel@tonic-gate 
27900Sstevel@tonic-gate 	if (vp->vn_children != NULL) {
27910Sstevel@tonic-gate 		vw->vw_current = vp->vn_children;
27920Sstevel@tonic-gate 		return (rval);
27930Sstevel@tonic-gate 	}
27940Sstevel@tonic-gate 
27950Sstevel@tonic-gate 	do {
27960Sstevel@tonic-gate 		vw->vw_current = vp->vn_sibling;
27970Sstevel@tonic-gate 		vp = vp->vn_parent;
27980Sstevel@tonic-gate 	} while (vw->vw_current == NULL && vp != NULL);
27990Sstevel@tonic-gate 
28000Sstevel@tonic-gate 	return (rval);
28010Sstevel@tonic-gate }
28020Sstevel@tonic-gate 
28030Sstevel@tonic-gate /*
28040Sstevel@tonic-gate  * The "vmem_postfix" walk walks the vmem arenas in post-fix order; all
28050Sstevel@tonic-gate  * children are visited before their parent.  We perform the postfix walk
28060Sstevel@tonic-gate  * iteratively (rather than recursively) to allow mdb to regain control
28070Sstevel@tonic-gate  * after each callback.
28080Sstevel@tonic-gate  */
28090Sstevel@tonic-gate int
28100Sstevel@tonic-gate vmem_postfix_walk_step(mdb_walk_state_t *wsp)
28110Sstevel@tonic-gate {
28120Sstevel@tonic-gate 	vmem_walk_t *vw = wsp->walk_data;
28130Sstevel@tonic-gate 	vmem_node_t *vp = vw->vw_current;
28140Sstevel@tonic-gate 	int rval;
28150Sstevel@tonic-gate 
28160Sstevel@tonic-gate 	/*
28170Sstevel@tonic-gate 	 * If this node is marked, then we know that we have already visited
28180Sstevel@tonic-gate 	 * all of its children.  If the node has any siblings, they need to
28190Sstevel@tonic-gate 	 * be visited next; otherwise, we need to visit the parent.  Note
28200Sstevel@tonic-gate 	 * that vp->vn_marked will only be zero on the first invocation of
28210Sstevel@tonic-gate 	 * the step function.
28220Sstevel@tonic-gate 	 */
28230Sstevel@tonic-gate 	if (vp->vn_marked) {
28240Sstevel@tonic-gate 		if (vp->vn_sibling != NULL)
28250Sstevel@tonic-gate 			vp = vp->vn_sibling;
28260Sstevel@tonic-gate 		else if (vp->vn_parent != NULL)
28270Sstevel@tonic-gate 			vp = vp->vn_parent;
28280Sstevel@tonic-gate 		else {
28290Sstevel@tonic-gate 			/*
28300Sstevel@tonic-gate 			 * We have neither a parent, nor a sibling, and we
28310Sstevel@tonic-gate 			 * have already been visited; we're done.
28320Sstevel@tonic-gate 			 */
28330Sstevel@tonic-gate 			return (WALK_DONE);
28340Sstevel@tonic-gate 		}
28350Sstevel@tonic-gate 	}
28360Sstevel@tonic-gate 
28370Sstevel@tonic-gate 	/*
28380Sstevel@tonic-gate 	 * Before we visit this node, visit its children.
28390Sstevel@tonic-gate 	 */
28400Sstevel@tonic-gate 	while (vp->vn_children != NULL && !vp->vn_children->vn_marked)
28410Sstevel@tonic-gate 		vp = vp->vn_children;
28420Sstevel@tonic-gate 
28430Sstevel@tonic-gate 	vp->vn_marked = 1;
28440Sstevel@tonic-gate 	vw->vw_current = vp;
28450Sstevel@tonic-gate 	rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata);
28460Sstevel@tonic-gate 
28470Sstevel@tonic-gate 	return (rval);
28480Sstevel@tonic-gate }
28490Sstevel@tonic-gate 
28500Sstevel@tonic-gate void
28510Sstevel@tonic-gate vmem_walk_fini(mdb_walk_state_t *wsp)
28520Sstevel@tonic-gate {
28530Sstevel@tonic-gate 	vmem_walk_t *vw = wsp->walk_data;
28540Sstevel@tonic-gate 	vmem_node_t *root = vw->vw_root;
28550Sstevel@tonic-gate 	int done;
28560Sstevel@tonic-gate 
28570Sstevel@tonic-gate 	if (root == NULL)
28580Sstevel@tonic-gate 		return;
28590Sstevel@tonic-gate 
28600Sstevel@tonic-gate 	if ((vw->vw_root = root->vn_children) != NULL)
28610Sstevel@tonic-gate 		vmem_walk_fini(wsp);
28620Sstevel@tonic-gate 
28630Sstevel@tonic-gate 	vw->vw_root = root->vn_sibling;
28640Sstevel@tonic-gate 	done = (root->vn_sibling == NULL && root->vn_parent == NULL);
28650Sstevel@tonic-gate 	mdb_free(root, sizeof (vmem_node_t));
28660Sstevel@tonic-gate 
28670Sstevel@tonic-gate 	if (done) {
28680Sstevel@tonic-gate 		mdb_free(vw, sizeof (vmem_walk_t));
28690Sstevel@tonic-gate 	} else {
28700Sstevel@tonic-gate 		vmem_walk_fini(wsp);
28710Sstevel@tonic-gate 	}
28720Sstevel@tonic-gate }
28730Sstevel@tonic-gate 
28740Sstevel@tonic-gate typedef struct vmem_seg_walk {
28750Sstevel@tonic-gate 	uint8_t vsw_type;
28760Sstevel@tonic-gate 	uintptr_t vsw_start;
28770Sstevel@tonic-gate 	uintptr_t vsw_current;
28780Sstevel@tonic-gate } vmem_seg_walk_t;
28790Sstevel@tonic-gate 
28800Sstevel@tonic-gate /*ARGSUSED*/
28810Sstevel@tonic-gate int
28820Sstevel@tonic-gate vmem_seg_walk_common_init(mdb_walk_state_t *wsp, uint8_t type, char *name)
28830Sstevel@tonic-gate {
28840Sstevel@tonic-gate 	vmem_seg_walk_t *vsw;
28850Sstevel@tonic-gate 
28860Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
28870Sstevel@tonic-gate 		mdb_warn("vmem_%s does not support global walks\n", name);
28880Sstevel@tonic-gate 		return (WALK_ERR);
28890Sstevel@tonic-gate 	}
28900Sstevel@tonic-gate 
28910Sstevel@tonic-gate 	wsp->walk_data = vsw = mdb_alloc(sizeof (vmem_seg_walk_t), UM_SLEEP);
28920Sstevel@tonic-gate 
28930Sstevel@tonic-gate 	vsw->vsw_type = type;
28940Sstevel@tonic-gate 	vsw->vsw_start = wsp->walk_addr + OFFSETOF(vmem_t, vm_seg0);
28950Sstevel@tonic-gate 	vsw->vsw_current = vsw->vsw_start;
28960Sstevel@tonic-gate 
28970Sstevel@tonic-gate 	return (WALK_NEXT);
28980Sstevel@tonic-gate }
28990Sstevel@tonic-gate 
29000Sstevel@tonic-gate /*
29010Sstevel@tonic-gate  * vmem segments can't have type 0 (this should be added to vmem_impl.h).
29020Sstevel@tonic-gate  */
29030Sstevel@tonic-gate #define	VMEM_NONE	0
29040Sstevel@tonic-gate 
29050Sstevel@tonic-gate int
29060Sstevel@tonic-gate vmem_alloc_walk_init(mdb_walk_state_t *wsp)
29070Sstevel@tonic-gate {
29080Sstevel@tonic-gate 	return (vmem_seg_walk_common_init(wsp, VMEM_ALLOC, "alloc"));
29090Sstevel@tonic-gate }
29100Sstevel@tonic-gate 
29110Sstevel@tonic-gate int
29120Sstevel@tonic-gate vmem_free_walk_init(mdb_walk_state_t *wsp)
29130Sstevel@tonic-gate {
29140Sstevel@tonic-gate 	return (vmem_seg_walk_common_init(wsp, VMEM_FREE, "free"));
29150Sstevel@tonic-gate }
29160Sstevel@tonic-gate 
29170Sstevel@tonic-gate int
29180Sstevel@tonic-gate vmem_span_walk_init(mdb_walk_state_t *wsp)
29190Sstevel@tonic-gate {
29200Sstevel@tonic-gate 	return (vmem_seg_walk_common_init(wsp, VMEM_SPAN, "span"));
29210Sstevel@tonic-gate }
29220Sstevel@tonic-gate 
29230Sstevel@tonic-gate int
29240Sstevel@tonic-gate vmem_seg_walk_init(mdb_walk_state_t *wsp)
29250Sstevel@tonic-gate {
29260Sstevel@tonic-gate 	return (vmem_seg_walk_common_init(wsp, VMEM_NONE, "seg"));
29270Sstevel@tonic-gate }
29280Sstevel@tonic-gate 
29290Sstevel@tonic-gate int
29300Sstevel@tonic-gate vmem_seg_walk_step(mdb_walk_state_t *wsp)
29310Sstevel@tonic-gate {
29320Sstevel@tonic-gate 	vmem_seg_t seg;
29330Sstevel@tonic-gate 	vmem_seg_walk_t *vsw = wsp->walk_data;
29340Sstevel@tonic-gate 	uintptr_t addr = vsw->vsw_current;
29350Sstevel@tonic-gate 	static size_t seg_size = 0;
29360Sstevel@tonic-gate 	int rval;
29370Sstevel@tonic-gate 
29380Sstevel@tonic-gate 	if (!seg_size) {
29390Sstevel@tonic-gate 		if (umem_readvar(&seg_size, "vmem_seg_size") == -1) {
29400Sstevel@tonic-gate 			mdb_warn("failed to read 'vmem_seg_size'");
29410Sstevel@tonic-gate 			seg_size = sizeof (vmem_seg_t);
29420Sstevel@tonic-gate 		}
29430Sstevel@tonic-gate 	}
29440Sstevel@tonic-gate 
29450Sstevel@tonic-gate 	if (seg_size < sizeof (seg))
29460Sstevel@tonic-gate 		bzero((caddr_t)&seg + seg_size, sizeof (seg) - seg_size);
29470Sstevel@tonic-gate 
29480Sstevel@tonic-gate 	if (mdb_vread(&seg, seg_size, addr) == -1) {
29490Sstevel@tonic-gate 		mdb_warn("couldn't read vmem_seg at %p", addr);
29500Sstevel@tonic-gate 		return (WALK_ERR);
29510Sstevel@tonic-gate 	}
29520Sstevel@tonic-gate 
29530Sstevel@tonic-gate 	vsw->vsw_current = (uintptr_t)seg.vs_anext;
29540Sstevel@tonic-gate 	if (vsw->vsw_type != VMEM_NONE && seg.vs_type != vsw->vsw_type) {
29550Sstevel@tonic-gate 		rval = WALK_NEXT;
29560Sstevel@tonic-gate 	} else {
29570Sstevel@tonic-gate 		rval = wsp->walk_callback(addr, &seg, wsp->walk_cbdata);
29580Sstevel@tonic-gate 	}
29590Sstevel@tonic-gate 
29600Sstevel@tonic-gate 	if (vsw->vsw_current == vsw->vsw_start)
29610Sstevel@tonic-gate 		return (WALK_DONE);
29620Sstevel@tonic-gate 
29630Sstevel@tonic-gate 	return (rval);
29640Sstevel@tonic-gate }
29650Sstevel@tonic-gate 
29660Sstevel@tonic-gate void
29670Sstevel@tonic-gate vmem_seg_walk_fini(mdb_walk_state_t *wsp)
29680Sstevel@tonic-gate {
29690Sstevel@tonic-gate 	vmem_seg_walk_t *vsw = wsp->walk_data;
29700Sstevel@tonic-gate 
29710Sstevel@tonic-gate 	mdb_free(vsw, sizeof (vmem_seg_walk_t));
29720Sstevel@tonic-gate }
29730Sstevel@tonic-gate 
29740Sstevel@tonic-gate #define	VMEM_NAMEWIDTH	22
29750Sstevel@tonic-gate 
29760Sstevel@tonic-gate int
29770Sstevel@tonic-gate vmem(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
29780Sstevel@tonic-gate {
29790Sstevel@tonic-gate 	vmem_t v, parent;
29800Sstevel@tonic-gate 	uintptr_t paddr;
29810Sstevel@tonic-gate 	int ident = 0;
29820Sstevel@tonic-gate 	char c[VMEM_NAMEWIDTH];
29830Sstevel@tonic-gate 
29840Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
29850Sstevel@tonic-gate 		if (mdb_walk_dcmd("vmem", "vmem", argc, argv) == -1) {
29860Sstevel@tonic-gate 			mdb_warn("can't walk vmem");
29870Sstevel@tonic-gate 			return (DCMD_ERR);
29880Sstevel@tonic-gate 		}
29890Sstevel@tonic-gate 		return (DCMD_OK);
29900Sstevel@tonic-gate 	}
29910Sstevel@tonic-gate 
29920Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags))
29930Sstevel@tonic-gate 		mdb_printf("%-?s %-*s %10s %12s %9s %5s\n",
29940Sstevel@tonic-gate 		    "ADDR", VMEM_NAMEWIDTH, "NAME", "INUSE",
29950Sstevel@tonic-gate 		    "TOTAL", "SUCCEED", "FAIL");
29960Sstevel@tonic-gate 
29970Sstevel@tonic-gate 	if (mdb_vread(&v, sizeof (v), addr) == -1) {
29980Sstevel@tonic-gate 		mdb_warn("couldn't read vmem at %p", addr);
29990Sstevel@tonic-gate 		return (DCMD_ERR);
30000Sstevel@tonic-gate 	}
30010Sstevel@tonic-gate 
30020Sstevel@tonic-gate 	for (paddr = (uintptr_t)v.vm_source; paddr != NULL; ident += 2) {
30030Sstevel@tonic-gate 		if (mdb_vread(&parent, sizeof (parent), paddr) == -1) {
30040Sstevel@tonic-gate 			mdb_warn("couldn't trace %p's ancestry", addr);
30050Sstevel@tonic-gate 			ident = 0;
30060Sstevel@tonic-gate 			break;
30070Sstevel@tonic-gate 		}
30080Sstevel@tonic-gate 		paddr = (uintptr_t)parent.vm_source;
30090Sstevel@tonic-gate 	}
30100Sstevel@tonic-gate 
30110Sstevel@tonic-gate 	(void) mdb_snprintf(c, VMEM_NAMEWIDTH, "%*s%s", ident, "", v.vm_name);
30120Sstevel@tonic-gate 
30130Sstevel@tonic-gate 	mdb_printf("%0?p %-*s %10llu %12llu %9llu %5llu\n",
30140Sstevel@tonic-gate 	    addr, VMEM_NAMEWIDTH, c,
30150Sstevel@tonic-gate 	    v.vm_kstat.vk_mem_inuse, v.vm_kstat.vk_mem_total,
30160Sstevel@tonic-gate 	    v.vm_kstat.vk_alloc, v.vm_kstat.vk_fail);
30170Sstevel@tonic-gate 
30180Sstevel@tonic-gate 	return (DCMD_OK);
30190Sstevel@tonic-gate }
30200Sstevel@tonic-gate 
30210Sstevel@tonic-gate void
30220Sstevel@tonic-gate vmem_seg_help(void)
30230Sstevel@tonic-gate {
30240Sstevel@tonic-gate 	mdb_printf("%s\n",
30250Sstevel@tonic-gate "Display the contents of vmem_seg_ts, with optional filtering.\n"
30260Sstevel@tonic-gate "\n"
30270Sstevel@tonic-gate "A vmem_seg_t represents a range of addresses (or arbitrary numbers),\n"
30280Sstevel@tonic-gate "representing a single chunk of data.  Only ALLOC segments have debugging\n"
30290Sstevel@tonic-gate "information.\n");
30300Sstevel@tonic-gate 	mdb_dec_indent(2);
30310Sstevel@tonic-gate 	mdb_printf("%<b>OPTIONS%</b>\n");
30320Sstevel@tonic-gate 	mdb_inc_indent(2);
30330Sstevel@tonic-gate 	mdb_printf("%s",
30340Sstevel@tonic-gate "  -v    Display the full content of the vmem_seg, including its stack trace\n"
30350Sstevel@tonic-gate "  -s    report the size of the segment, instead of the end address\n"
30360Sstevel@tonic-gate "  -c caller\n"
30370Sstevel@tonic-gate "        filter out segments without the function/PC in their stack trace\n"
30380Sstevel@tonic-gate "  -e earliest\n"
30390Sstevel@tonic-gate "        filter out segments timestamped before earliest\n"
30400Sstevel@tonic-gate "  -l latest\n"
30410Sstevel@tonic-gate "        filter out segments timestamped after latest\n"
30420Sstevel@tonic-gate "  -m minsize\n"
30430Sstevel@tonic-gate "        filer out segments smaller than minsize\n"
30440Sstevel@tonic-gate "  -M maxsize\n"
30450Sstevel@tonic-gate "        filer out segments larger than maxsize\n"
30460Sstevel@tonic-gate "  -t thread\n"
30470Sstevel@tonic-gate "        filter out segments not involving thread\n"
30480Sstevel@tonic-gate "  -T type\n"
30490Sstevel@tonic-gate "        filter out segments not of type 'type'\n"
30500Sstevel@tonic-gate "        type is one of: ALLOC/FREE/SPAN/ROTOR/WALKER\n");
30510Sstevel@tonic-gate }
30520Sstevel@tonic-gate 
30530Sstevel@tonic-gate 
30540Sstevel@tonic-gate /*ARGSUSED*/
30550Sstevel@tonic-gate int
30560Sstevel@tonic-gate vmem_seg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
30570Sstevel@tonic-gate {
30580Sstevel@tonic-gate 	vmem_seg_t vs;
30590Sstevel@tonic-gate 	uintptr_t *stk = vs.vs_stack;
30600Sstevel@tonic-gate 	uintptr_t sz;
30610Sstevel@tonic-gate 	uint8_t t;
30620Sstevel@tonic-gate 	const char *type = NULL;
30630Sstevel@tonic-gate 	GElf_Sym sym;
30640Sstevel@tonic-gate 	char c[MDB_SYM_NAMLEN];
30650Sstevel@tonic-gate 	int no_debug;
30660Sstevel@tonic-gate 	int i;
30670Sstevel@tonic-gate 	int depth;
30680Sstevel@tonic-gate 	uintptr_t laddr, haddr;
30690Sstevel@tonic-gate 
30700Sstevel@tonic-gate 	uintptr_t caller = NULL, thread = NULL;
30710Sstevel@tonic-gate 	uintptr_t minsize = 0, maxsize = 0;
30720Sstevel@tonic-gate 
30730Sstevel@tonic-gate 	hrtime_t earliest = 0, latest = 0;
30740Sstevel@tonic-gate 
30750Sstevel@tonic-gate 	uint_t size = 0;
30760Sstevel@tonic-gate 	uint_t verbose = 0;
30770Sstevel@tonic-gate 
30780Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
30790Sstevel@tonic-gate 		return (DCMD_USAGE);
30800Sstevel@tonic-gate 
30810Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
30820Sstevel@tonic-gate 	    'c', MDB_OPT_UINTPTR, &caller,
30830Sstevel@tonic-gate 	    'e', MDB_OPT_UINT64, &earliest,
30840Sstevel@tonic-gate 	    'l', MDB_OPT_UINT64, &latest,
30850Sstevel@tonic-gate 	    's', MDB_OPT_SETBITS, TRUE, &size,
30860Sstevel@tonic-gate 	    'm', MDB_OPT_UINTPTR, &minsize,
30870Sstevel@tonic-gate 	    'M', MDB_OPT_UINTPTR, &maxsize,
30880Sstevel@tonic-gate 	    't', MDB_OPT_UINTPTR, &thread,
30890Sstevel@tonic-gate 	    'T', MDB_OPT_STR, &type,
30900Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
30910Sstevel@tonic-gate 	    NULL) != argc)
30920Sstevel@tonic-gate 		return (DCMD_USAGE);
30930Sstevel@tonic-gate 
30940Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
30950Sstevel@tonic-gate 		if (verbose) {
30960Sstevel@tonic-gate 			mdb_printf("%16s %4s %16s %16s %16s\n"
30970Sstevel@tonic-gate 			    "%<u>%16s %4s %16s %16s %16s%</u>\n",
30980Sstevel@tonic-gate 			    "ADDR", "TYPE", "START", "END", "SIZE",
30990Sstevel@tonic-gate 			    "", "", "THREAD", "TIMESTAMP", "");
31000Sstevel@tonic-gate 		} else {
31010Sstevel@tonic-gate 			mdb_printf("%?s %4s %?s %?s %s\n", "ADDR", "TYPE",
31020Sstevel@tonic-gate 			    "START", size? "SIZE" : "END", "WHO");
31030Sstevel@tonic-gate 		}
31040Sstevel@tonic-gate 	}
31050Sstevel@tonic-gate 
31060Sstevel@tonic-gate 	if (mdb_vread(&vs, sizeof (vs), addr) == -1) {
31070Sstevel@tonic-gate 		mdb_warn("couldn't read vmem_seg at %p", addr);
31080Sstevel@tonic-gate 		return (DCMD_ERR);
31090Sstevel@tonic-gate 	}
31100Sstevel@tonic-gate 
31110Sstevel@tonic-gate 	if (type != NULL) {
31120Sstevel@tonic-gate 		if (strcmp(type, "ALLC") == 0 || strcmp(type, "ALLOC") == 0)
31130Sstevel@tonic-gate 			t = VMEM_ALLOC;
31140Sstevel@tonic-gate 		else if (strcmp(type, "FREE") == 0)
31150Sstevel@tonic-gate 			t = VMEM_FREE;
31160Sstevel@tonic-gate 		else if (strcmp(type, "SPAN") == 0)
31170Sstevel@tonic-gate 			t = VMEM_SPAN;
31180Sstevel@tonic-gate 		else if (strcmp(type, "ROTR") == 0 ||
31190Sstevel@tonic-gate 		    strcmp(type, "ROTOR") == 0)
31200Sstevel@tonic-gate 			t = VMEM_ROTOR;
31210Sstevel@tonic-gate 		else if (strcmp(type, "WLKR") == 0 ||
31220Sstevel@tonic-gate 		    strcmp(type, "WALKER") == 0)
31230Sstevel@tonic-gate 			t = VMEM_WALKER;
31240Sstevel@tonic-gate 		else {
31250Sstevel@tonic-gate 			mdb_warn("\"%s\" is not a recognized vmem_seg type\n",
31260Sstevel@tonic-gate 			    type);
31270Sstevel@tonic-gate 			return (DCMD_ERR);
31280Sstevel@tonic-gate 		}
31290Sstevel@tonic-gate 
31300Sstevel@tonic-gate 		if (vs.vs_type != t)
31310Sstevel@tonic-gate 			return (DCMD_OK);
31320Sstevel@tonic-gate 	}
31330Sstevel@tonic-gate 
31340Sstevel@tonic-gate 	sz = vs.vs_end - vs.vs_start;
31350Sstevel@tonic-gate 
31360Sstevel@tonic-gate 	if (minsize != 0 && sz < minsize)
31370Sstevel@tonic-gate 		return (DCMD_OK);
31380Sstevel@tonic-gate 
31390Sstevel@tonic-gate 	if (maxsize != 0 && sz > maxsize)
31400Sstevel@tonic-gate 		return (DCMD_OK);
31410Sstevel@tonic-gate 
31420Sstevel@tonic-gate 	t = vs.vs_type;
31430Sstevel@tonic-gate 	depth = vs.vs_depth;
31440Sstevel@tonic-gate 
31450Sstevel@tonic-gate 	/*
31460Sstevel@tonic-gate 	 * debug info, when present, is only accurate for VMEM_ALLOC segments
31470Sstevel@tonic-gate 	 */
31480Sstevel@tonic-gate 	no_debug = (t != VMEM_ALLOC) ||
31490Sstevel@tonic-gate 	    (depth == 0 || depth > VMEM_STACK_DEPTH);
31500Sstevel@tonic-gate 
31510Sstevel@tonic-gate 	if (no_debug) {
31520Sstevel@tonic-gate 		if (caller != NULL || thread != NULL || earliest != 0 ||
31530Sstevel@tonic-gate 		    latest != 0)
31540Sstevel@tonic-gate 			return (DCMD_OK);		/* not enough info */
31550Sstevel@tonic-gate 	} else {
31560Sstevel@tonic-gate 		if (caller != NULL) {
31570Sstevel@tonic-gate 			laddr = caller;
31580Sstevel@tonic-gate 			haddr = caller + sizeof (caller);
31590Sstevel@tonic-gate 
31600Sstevel@tonic-gate 			if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c,
31610Sstevel@tonic-gate 			    sizeof (c), &sym) != -1 &&
31620Sstevel@tonic-gate 			    caller == (uintptr_t)sym.st_value) {
31630Sstevel@tonic-gate 				/*
31640Sstevel@tonic-gate 				 * We were provided an exact symbol value; any
31650Sstevel@tonic-gate 				 * address in the function is valid.
31660Sstevel@tonic-gate 				 */
31670Sstevel@tonic-gate 				laddr = (uintptr_t)sym.st_value;
31680Sstevel@tonic-gate 				haddr = (uintptr_t)sym.st_value + sym.st_size;
31690Sstevel@tonic-gate 			}
31700Sstevel@tonic-gate 
31710Sstevel@tonic-gate 			for (i = 0; i < depth; i++)
31720Sstevel@tonic-gate 				if (vs.vs_stack[i] >= laddr &&
31730Sstevel@tonic-gate 				    vs.vs_stack[i] < haddr)
31740Sstevel@tonic-gate 					break;
31750Sstevel@tonic-gate 
31760Sstevel@tonic-gate 			if (i == depth)
31770Sstevel@tonic-gate 				return (DCMD_OK);
31780Sstevel@tonic-gate 		}
31790Sstevel@tonic-gate 
31800Sstevel@tonic-gate 		if (thread != NULL && (uintptr_t)vs.vs_thread != thread)
31810Sstevel@tonic-gate 			return (DCMD_OK);
31820Sstevel@tonic-gate 
31830Sstevel@tonic-gate 		if (earliest != 0 && vs.vs_timestamp < earliest)
31840Sstevel@tonic-gate 			return (DCMD_OK);
31850Sstevel@tonic-gate 
31860Sstevel@tonic-gate 		if (latest != 0 && vs.vs_timestamp > latest)
31870Sstevel@tonic-gate 			return (DCMD_OK);
31880Sstevel@tonic-gate 	}
31890Sstevel@tonic-gate 
31900Sstevel@tonic-gate 	type = (t == VMEM_ALLOC ? "ALLC" :
31910Sstevel@tonic-gate 	    t == VMEM_FREE ? "FREE" :
31920Sstevel@tonic-gate 	    t == VMEM_SPAN ? "SPAN" :
31930Sstevel@tonic-gate 	    t == VMEM_ROTOR ? "ROTR" :
31940Sstevel@tonic-gate 	    t == VMEM_WALKER ? "WLKR" :
31950Sstevel@tonic-gate 	    "????");
31960Sstevel@tonic-gate 
31970Sstevel@tonic-gate 	if (flags & DCMD_PIPE_OUT) {
31980Sstevel@tonic-gate 		mdb_printf("%#r\n", addr);
31990Sstevel@tonic-gate 		return (DCMD_OK);
32000Sstevel@tonic-gate 	}
32010Sstevel@tonic-gate 
32020Sstevel@tonic-gate 	if (verbose) {
32030Sstevel@tonic-gate 		mdb_printf("%<b>%16p%</b> %4s %16p %16p %16d\n",
32040Sstevel@tonic-gate 		    addr, type, vs.vs_start, vs.vs_end, sz);
32050Sstevel@tonic-gate 
32060Sstevel@tonic-gate 		if (no_debug)
32070Sstevel@tonic-gate 			return (DCMD_OK);
32080Sstevel@tonic-gate 
32090Sstevel@tonic-gate 		mdb_printf("%16s %4s %16d %16llx\n",
32100Sstevel@tonic-gate 		    "", "", vs.vs_thread, vs.vs_timestamp);
32110Sstevel@tonic-gate 
32120Sstevel@tonic-gate 		mdb_inc_indent(17);
32130Sstevel@tonic-gate 		for (i = 0; i < depth; i++) {
32140Sstevel@tonic-gate 			mdb_printf("%a\n", stk[i]);
32150Sstevel@tonic-gate 		}
32160Sstevel@tonic-gate 		mdb_dec_indent(17);
32170Sstevel@tonic-gate 		mdb_printf("\n");
32180Sstevel@tonic-gate 	} else {
32190Sstevel@tonic-gate 		mdb_printf("%0?p %4s %0?p %0?p", addr, type,
32200Sstevel@tonic-gate 		    vs.vs_start, size? sz : vs.vs_end);
32210Sstevel@tonic-gate 
32220Sstevel@tonic-gate 		if (no_debug) {
32230Sstevel@tonic-gate 			mdb_printf("\n");
32240Sstevel@tonic-gate 			return (DCMD_OK);
32250Sstevel@tonic-gate 		}
32260Sstevel@tonic-gate 
32270Sstevel@tonic-gate 		for (i = 0; i < depth; i++) {
32280Sstevel@tonic-gate 			if (mdb_lookup_by_addr(stk[i], MDB_SYM_FUZZY,
32290Sstevel@tonic-gate 			    c, sizeof (c), &sym) == -1)
32300Sstevel@tonic-gate 				continue;
32310Sstevel@tonic-gate 			if (is_umem_sym(c, "vmem_"))
32320Sstevel@tonic-gate 				continue;
32330Sstevel@tonic-gate 			break;
32340Sstevel@tonic-gate 		}
32350Sstevel@tonic-gate 		mdb_printf(" %a\n", stk[i]);
32360Sstevel@tonic-gate 	}
32370Sstevel@tonic-gate 	return (DCMD_OK);
32380Sstevel@tonic-gate }
32390Sstevel@tonic-gate 
32400Sstevel@tonic-gate /*ARGSUSED*/
32410Sstevel@tonic-gate static int
32420Sstevel@tonic-gate showbc(uintptr_t addr, const umem_bufctl_audit_t *bcp, hrtime_t *newest)
32430Sstevel@tonic-gate {
32440Sstevel@tonic-gate 	char name[UMEM_CACHE_NAMELEN + 1];
32450Sstevel@tonic-gate 	hrtime_t delta;
32460Sstevel@tonic-gate 	int i, depth;
32470Sstevel@tonic-gate 
32480Sstevel@tonic-gate 	if (bcp->bc_timestamp == 0)
32490Sstevel@tonic-gate 		return (WALK_DONE);
32500Sstevel@tonic-gate 
32510Sstevel@tonic-gate 	if (*newest == 0)
32520Sstevel@tonic-gate 		*newest = bcp->bc_timestamp;
32530Sstevel@tonic-gate 
32540Sstevel@tonic-gate 	delta = *newest - bcp->bc_timestamp;
32550Sstevel@tonic-gate 	depth = MIN(bcp->bc_depth, umem_stack_depth);
32560Sstevel@tonic-gate 
32570Sstevel@tonic-gate 	if (mdb_readstr(name, sizeof (name), (uintptr_t)
32580Sstevel@tonic-gate 	    &bcp->bc_cache->cache_name) <= 0)
32590Sstevel@tonic-gate 		(void) mdb_snprintf(name, sizeof (name), "%a", bcp->bc_cache);
32600Sstevel@tonic-gate 
32610Sstevel@tonic-gate 	mdb_printf("\nT-%lld.%09lld  addr=%p  %s\n",
32620Sstevel@tonic-gate 	    delta / NANOSEC, delta % NANOSEC, bcp->bc_addr, name);
32630Sstevel@tonic-gate 
32640Sstevel@tonic-gate 	for (i = 0; i < depth; i++)
32650Sstevel@tonic-gate 		mdb_printf("\t %a\n", bcp->bc_stack[i]);
32660Sstevel@tonic-gate 
32670Sstevel@tonic-gate 	return (WALK_NEXT);
32680Sstevel@tonic-gate }
32690Sstevel@tonic-gate 
32700Sstevel@tonic-gate int
32710Sstevel@tonic-gate umalog(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
32720Sstevel@tonic-gate {
32730Sstevel@tonic-gate 	const char *logname = "umem_transaction_log";
32740Sstevel@tonic-gate 	hrtime_t newest = 0;
32750Sstevel@tonic-gate 
32760Sstevel@tonic-gate 	if ((flags & DCMD_ADDRSPEC) || argc > 1)
32770Sstevel@tonic-gate 		return (DCMD_USAGE);
32780Sstevel@tonic-gate 
32790Sstevel@tonic-gate 	if (argc > 0) {
32800Sstevel@tonic-gate 		if (argv->a_type != MDB_TYPE_STRING)
32810Sstevel@tonic-gate 			return (DCMD_USAGE);
32820Sstevel@tonic-gate 		if (strcmp(argv->a_un.a_str, "fail") == 0)
32830Sstevel@tonic-gate 			logname = "umem_failure_log";
32840Sstevel@tonic-gate 		else if (strcmp(argv->a_un.a_str, "slab") == 0)
32850Sstevel@tonic-gate 			logname = "umem_slab_log";
32860Sstevel@tonic-gate 		else
32870Sstevel@tonic-gate 			return (DCMD_USAGE);
32880Sstevel@tonic-gate 	}
32890Sstevel@tonic-gate 
32900Sstevel@tonic-gate 	if (umem_readvar(&addr, logname) == -1) {
32910Sstevel@tonic-gate 		mdb_warn("failed to read %s log header pointer");
32920Sstevel@tonic-gate 		return (DCMD_ERR);
32930Sstevel@tonic-gate 	}
32940Sstevel@tonic-gate 
32950Sstevel@tonic-gate 	if (mdb_pwalk("umem_log", (mdb_walk_cb_t)showbc, &newest, addr) == -1) {
32960Sstevel@tonic-gate 		mdb_warn("failed to walk umem log");
32970Sstevel@tonic-gate 		return (DCMD_ERR);
32980Sstevel@tonic-gate 	}
32990Sstevel@tonic-gate 
33000Sstevel@tonic-gate 	return (DCMD_OK);
33010Sstevel@tonic-gate }
33020Sstevel@tonic-gate 
33030Sstevel@tonic-gate /*
33040Sstevel@tonic-gate  * As the final lure for die-hard crash(1M) users, we provide ::umausers here.
33050Sstevel@tonic-gate  * The first piece is a structure which we use to accumulate umem_cache_t
33060Sstevel@tonic-gate  * addresses of interest.  The umc_add is used as a callback for the umem_cache
33070Sstevel@tonic-gate  * walker; we either add all caches, or ones named explicitly as arguments.
33080Sstevel@tonic-gate  */
33090Sstevel@tonic-gate 
33100Sstevel@tonic-gate typedef struct umclist {
33110Sstevel@tonic-gate 	const char *umc_name;			/* Name to match (or NULL) */
33120Sstevel@tonic-gate 	uintptr_t *umc_caches;			/* List of umem_cache_t addrs */
33130Sstevel@tonic-gate 	int umc_nelems;				/* Num entries in umc_caches */
33140Sstevel@tonic-gate 	int umc_size;				/* Size of umc_caches array */
33150Sstevel@tonic-gate } umclist_t;
33160Sstevel@tonic-gate 
33170Sstevel@tonic-gate static int
33180Sstevel@tonic-gate umc_add(uintptr_t addr, const umem_cache_t *cp, umclist_t *umc)
33190Sstevel@tonic-gate {
33200Sstevel@tonic-gate 	void *p;
33210Sstevel@tonic-gate 	int s;
33220Sstevel@tonic-gate 
33230Sstevel@tonic-gate 	if (umc->umc_name == NULL ||
33240Sstevel@tonic-gate 	    strcmp(cp->cache_name, umc->umc_name) == 0) {
33250Sstevel@tonic-gate 		/*
33260Sstevel@tonic-gate 		 * If we have a match, grow our array (if necessary), and then
33270Sstevel@tonic-gate 		 * add the virtual address of the matching cache to our list.
33280Sstevel@tonic-gate 		 */
33290Sstevel@tonic-gate 		if (umc->umc_nelems >= umc->umc_size) {
33300Sstevel@tonic-gate 			s = umc->umc_size ? umc->umc_size * 2 : 256;
33310Sstevel@tonic-gate 			p = mdb_alloc(sizeof (uintptr_t) * s, UM_SLEEP | UM_GC);
33320Sstevel@tonic-gate 
33330Sstevel@tonic-gate 			bcopy(umc->umc_caches, p,
33340Sstevel@tonic-gate 			    sizeof (uintptr_t) * umc->umc_size);
33350Sstevel@tonic-gate 
33360Sstevel@tonic-gate 			umc->umc_caches = p;
33370Sstevel@tonic-gate 			umc->umc_size = s;
33380Sstevel@tonic-gate 		}
33390Sstevel@tonic-gate 
33400Sstevel@tonic-gate 		umc->umc_caches[umc->umc_nelems++] = addr;
33410Sstevel@tonic-gate 		return (umc->umc_name ? WALK_DONE : WALK_NEXT);
33420Sstevel@tonic-gate 	}
33430Sstevel@tonic-gate 
33440Sstevel@tonic-gate 	return (WALK_NEXT);
33450Sstevel@tonic-gate }
33460Sstevel@tonic-gate 
33470Sstevel@tonic-gate /*
33480Sstevel@tonic-gate  * The second piece of ::umausers is a hash table of allocations.  Each
33490Sstevel@tonic-gate  * allocation owner is identified by its stack trace and data_size.  We then
33500Sstevel@tonic-gate  * track the total bytes of all such allocations, and the number of allocations
33510Sstevel@tonic-gate  * to report at the end.  Once we have a list of caches, we walk through the
33520Sstevel@tonic-gate  * allocated bufctls of each, and update our hash table accordingly.
33530Sstevel@tonic-gate  */
33540Sstevel@tonic-gate 
33550Sstevel@tonic-gate typedef struct umowner {
33560Sstevel@tonic-gate 	struct umowner *umo_head;		/* First hash elt in bucket */
33570Sstevel@tonic-gate 	struct umowner *umo_next;		/* Next hash elt in chain */
33580Sstevel@tonic-gate 	size_t umo_signature;			/* Hash table signature */
33590Sstevel@tonic-gate 	uint_t umo_num;				/* Number of allocations */
33600Sstevel@tonic-gate 	size_t umo_data_size;			/* Size of each allocation */
33610Sstevel@tonic-gate 	size_t umo_total_size;			/* Total bytes of allocation */
33620Sstevel@tonic-gate 	int umo_depth;				/* Depth of stack trace */
33630Sstevel@tonic-gate 	uintptr_t *umo_stack;			/* Stack trace */
33640Sstevel@tonic-gate } umowner_t;
33650Sstevel@tonic-gate 
33660Sstevel@tonic-gate typedef struct umusers {
33670Sstevel@tonic-gate 	const umem_cache_t *umu_cache;		/* Current umem cache */
33680Sstevel@tonic-gate 	umowner_t *umu_hash;			/* Hash table of owners */
33690Sstevel@tonic-gate 	uintptr_t *umu_stacks;			/* stacks for owners */
33700Sstevel@tonic-gate 	int umu_nelems;				/* Number of entries in use */
33710Sstevel@tonic-gate 	int umu_size;				/* Total number of entries */
33720Sstevel@tonic-gate } umusers_t;
33730Sstevel@tonic-gate 
33740Sstevel@tonic-gate static void
33750Sstevel@tonic-gate umu_add(umusers_t *umu, const umem_bufctl_audit_t *bcp,
33760Sstevel@tonic-gate     size_t size, size_t data_size)
33770Sstevel@tonic-gate {
33780Sstevel@tonic-gate 	int i, depth = MIN(bcp->bc_depth, umem_stack_depth);
33790Sstevel@tonic-gate 	size_t bucket, signature = data_size;
33800Sstevel@tonic-gate 	umowner_t *umo, *umoend;
33810Sstevel@tonic-gate 
33820Sstevel@tonic-gate 	/*
33830Sstevel@tonic-gate 	 * If the hash table is full, double its size and rehash everything.
33840Sstevel@tonic-gate 	 */
33850Sstevel@tonic-gate 	if (umu->umu_nelems >= umu->umu_size) {
33860Sstevel@tonic-gate 		int s = umu->umu_size ? umu->umu_size * 2 : 1024;
33870Sstevel@tonic-gate 		size_t umowner_size = sizeof (umowner_t);
33880Sstevel@tonic-gate 		size_t trace_size = umem_stack_depth * sizeof (uintptr_t);
33890Sstevel@tonic-gate 		uintptr_t *new_stacks;
33900Sstevel@tonic-gate 
33910Sstevel@tonic-gate 		umo = mdb_alloc(umowner_size * s, UM_SLEEP | UM_GC);
33920Sstevel@tonic-gate 		new_stacks = mdb_alloc(trace_size * s, UM_SLEEP | UM_GC);
33930Sstevel@tonic-gate 
33940Sstevel@tonic-gate 		bcopy(umu->umu_hash, umo, umowner_size * umu->umu_size);
33950Sstevel@tonic-gate 		bcopy(umu->umu_stacks, new_stacks, trace_size * umu->umu_size);
33960Sstevel@tonic-gate 		umu->umu_hash = umo;
33970Sstevel@tonic-gate 		umu->umu_stacks = new_stacks;
33980Sstevel@tonic-gate 		umu->umu_size = s;
33990Sstevel@tonic-gate 
34000Sstevel@tonic-gate 		umoend = umu->umu_hash + umu->umu_size;
34010Sstevel@tonic-gate 		for (umo = umu->umu_hash; umo < umoend; umo++) {
34020Sstevel@tonic-gate 			umo->umo_head = NULL;
34030Sstevel@tonic-gate 			umo->umo_stack = &umu->umu_stacks[
34040Sstevel@tonic-gate 			    umem_stack_depth * (umo - umu->umu_hash)];
34050Sstevel@tonic-gate 		}
34060Sstevel@tonic-gate 
34070Sstevel@tonic-gate 		umoend = umu->umu_hash + umu->umu_nelems;
34080Sstevel@tonic-gate 		for (umo = umu->umu_hash; umo < umoend; umo++) {
34090Sstevel@tonic-gate 			bucket = umo->umo_signature & (umu->umu_size - 1);
34100Sstevel@tonic-gate 			umo->umo_next = umu->umu_hash[bucket].umo_head;
34110Sstevel@tonic-gate 			umu->umu_hash[bucket].umo_head = umo;
34120Sstevel@tonic-gate 		}
34130Sstevel@tonic-gate 	}
34140Sstevel@tonic-gate 
34150Sstevel@tonic-gate 	/*
34160Sstevel@tonic-gate 	 * Finish computing the hash signature from the stack trace, and then
34170Sstevel@tonic-gate 	 * see if the owner is in the hash table.  If so, update our stats.
34180Sstevel@tonic-gate 	 */
34190Sstevel@tonic-gate 	for (i = 0; i < depth; i++)
34200Sstevel@tonic-gate 		signature += bcp->bc_stack[i];
34210Sstevel@tonic-gate 
34220Sstevel@tonic-gate 	bucket = signature & (umu->umu_size - 1);
34230Sstevel@tonic-gate 
34240Sstevel@tonic-gate 	for (umo = umu->umu_hash[bucket].umo_head; umo; umo = umo->umo_next) {
34250Sstevel@tonic-gate 		if (umo->umo_signature == signature) {
34260Sstevel@tonic-gate 			size_t difference = 0;
34270Sstevel@tonic-gate 
34280Sstevel@tonic-gate 			difference |= umo->umo_data_size - data_size;
34290Sstevel@tonic-gate 			difference |= umo->umo_depth - depth;
34300Sstevel@tonic-gate 
34310Sstevel@tonic-gate 			for (i = 0; i < depth; i++) {
34320Sstevel@tonic-gate 				difference |= umo->umo_stack[i] -
34330Sstevel@tonic-gate 				    bcp->bc_stack[i];
34340Sstevel@tonic-gate 			}
34350Sstevel@tonic-gate 
34360Sstevel@tonic-gate 			if (difference == 0) {
34370Sstevel@tonic-gate 				umo->umo_total_size += size;
34380Sstevel@tonic-gate 				umo->umo_num++;
34390Sstevel@tonic-gate 				return;
34400Sstevel@tonic-gate 			}
34410Sstevel@tonic-gate 		}
34420Sstevel@tonic-gate 	}
34430Sstevel@tonic-gate 
34440Sstevel@tonic-gate 	/*
34450Sstevel@tonic-gate 	 * If the owner is not yet hashed, grab the next element and fill it
34460Sstevel@tonic-gate 	 * in based on the allocation information.
34470Sstevel@tonic-gate 	 */
34480Sstevel@tonic-gate 	umo = &umu->umu_hash[umu->umu_nelems++];
34490Sstevel@tonic-gate 	umo->umo_next = umu->umu_hash[bucket].umo_head;
34500Sstevel@tonic-gate 	umu->umu_hash[bucket].umo_head = umo;
34510Sstevel@tonic-gate 
34520Sstevel@tonic-gate 	umo->umo_signature = signature;
34530Sstevel@tonic-gate 	umo->umo_num = 1;
34540Sstevel@tonic-gate 	umo->umo_data_size = data_size;
34550Sstevel@tonic-gate 	umo->umo_total_size = size;
34560Sstevel@tonic-gate 	umo->umo_depth = depth;
34570Sstevel@tonic-gate 
34580Sstevel@tonic-gate 	for (i = 0; i < depth; i++)
34590Sstevel@tonic-gate 		umo->umo_stack[i] = bcp->bc_stack[i];
34600Sstevel@tonic-gate }
34610Sstevel@tonic-gate 
34620Sstevel@tonic-gate /*
34630Sstevel@tonic-gate  * When ::umausers is invoked without the -f flag, we simply update our hash
34640Sstevel@tonic-gate  * table with the information from each allocated bufctl.
34650Sstevel@tonic-gate  */
34660Sstevel@tonic-gate /*ARGSUSED*/
34670Sstevel@tonic-gate static int
34680Sstevel@tonic-gate umause1(uintptr_t addr, const umem_bufctl_audit_t *bcp, umusers_t *umu)
34690Sstevel@tonic-gate {
34700Sstevel@tonic-gate 	const umem_cache_t *cp = umu->umu_cache;
34710Sstevel@tonic-gate 
34720Sstevel@tonic-gate 	umu_add(umu, bcp, cp->cache_bufsize, cp->cache_bufsize);
34730Sstevel@tonic-gate 	return (WALK_NEXT);
34740Sstevel@tonic-gate }
34750Sstevel@tonic-gate 
34760Sstevel@tonic-gate /*
34770Sstevel@tonic-gate  * When ::umausers is invoked with the -f flag, we print out the information
34780Sstevel@tonic-gate  * for each bufctl as well as updating the hash table.
34790Sstevel@tonic-gate  */
34800Sstevel@tonic-gate static int
34810Sstevel@tonic-gate umause2(uintptr_t addr, const umem_bufctl_audit_t *bcp, umusers_t *umu)
34820Sstevel@tonic-gate {
34830Sstevel@tonic-gate 	int i, depth = MIN(bcp->bc_depth, umem_stack_depth);
34840Sstevel@tonic-gate 	const umem_cache_t *cp = umu->umu_cache;
34850Sstevel@tonic-gate 
34860Sstevel@tonic-gate 	mdb_printf("size %d, addr %p, thread %p, cache %s\n",
34870Sstevel@tonic-gate 	    cp->cache_bufsize, addr, bcp->bc_thread, cp->cache_name);
34880Sstevel@tonic-gate 
34890Sstevel@tonic-gate 	for (i = 0; i < depth; i++)
34900Sstevel@tonic-gate 		mdb_printf("\t %a\n", bcp->bc_stack[i]);
34910Sstevel@tonic-gate 
34920Sstevel@tonic-gate 	umu_add(umu, bcp, cp->cache_bufsize, cp->cache_bufsize);
34930Sstevel@tonic-gate 	return (WALK_NEXT);
34940Sstevel@tonic-gate }
34950Sstevel@tonic-gate 
34960Sstevel@tonic-gate /*
34970Sstevel@tonic-gate  * We sort our results by allocation size before printing them.
34980Sstevel@tonic-gate  */
34990Sstevel@tonic-gate static int
35000Sstevel@tonic-gate umownercmp(const void *lp, const void *rp)
35010Sstevel@tonic-gate {
35020Sstevel@tonic-gate 	const umowner_t *lhs = lp;
35030Sstevel@tonic-gate 	const umowner_t *rhs = rp;
35040Sstevel@tonic-gate 
35050Sstevel@tonic-gate 	return (rhs->umo_total_size - lhs->umo_total_size);
35060Sstevel@tonic-gate }
35070Sstevel@tonic-gate 
35080Sstevel@tonic-gate /*
35090Sstevel@tonic-gate  * The main engine of ::umausers is relatively straightforward: First we
35100Sstevel@tonic-gate  * accumulate our list of umem_cache_t addresses into the umclist_t. Next we
35110Sstevel@tonic-gate  * iterate over the allocated bufctls of each cache in the list.  Finally,
35120Sstevel@tonic-gate  * we sort and print our results.
35130Sstevel@tonic-gate  */
35140Sstevel@tonic-gate /*ARGSUSED*/
35150Sstevel@tonic-gate int
35160Sstevel@tonic-gate umausers(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
35170Sstevel@tonic-gate {
35180Sstevel@tonic-gate 	int mem_threshold = 8192;	/* Minimum # bytes for printing */
35190Sstevel@tonic-gate 	int cnt_threshold = 100;	/* Minimum # blocks for printing */
35200Sstevel@tonic-gate 	int audited_caches = 0;		/* Number of UMF_AUDIT caches found */
35210Sstevel@tonic-gate 	int do_all_caches = 1;		/* Do all caches (no arguments) */
35220Sstevel@tonic-gate 	int opt_e = FALSE;		/* Include "small" users */
35230Sstevel@tonic-gate 	int opt_f = FALSE;		/* Print stack traces */
35240Sstevel@tonic-gate 
35250Sstevel@tonic-gate 	mdb_walk_cb_t callback = (mdb_walk_cb_t)umause1;
35260Sstevel@tonic-gate 	umowner_t *umo, *umoend;
35270Sstevel@tonic-gate 	int i, oelems;
35280Sstevel@tonic-gate 
35290Sstevel@tonic-gate 	umclist_t umc;
35300Sstevel@tonic-gate 	umusers_t umu;
35310Sstevel@tonic-gate 
35320Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
35330Sstevel@tonic-gate 		return (DCMD_USAGE);
35340Sstevel@tonic-gate 
35350Sstevel@tonic-gate 	bzero(&umc, sizeof (umc));
35360Sstevel@tonic-gate 	bzero(&umu, sizeof (umu));
35370Sstevel@tonic-gate 
35380Sstevel@tonic-gate 	while ((i = mdb_getopts(argc, argv,
35390Sstevel@tonic-gate 	    'e', MDB_OPT_SETBITS, TRUE, &opt_e,
35400Sstevel@tonic-gate 	    'f', MDB_OPT_SETBITS, TRUE, &opt_f, NULL)) != argc) {
35410Sstevel@tonic-gate 
35420Sstevel@tonic-gate 		argv += i;	/* skip past options we just processed */
35430Sstevel@tonic-gate 		argc -= i;	/* adjust argc */
35440Sstevel@tonic-gate 
35450Sstevel@tonic-gate 		if (argv->a_type != MDB_TYPE_STRING || *argv->a_un.a_str == '-')
35460Sstevel@tonic-gate 			return (DCMD_USAGE);
35470Sstevel@tonic-gate 
35480Sstevel@tonic-gate 		oelems = umc.umc_nelems;
35490Sstevel@tonic-gate 		umc.umc_name = argv->a_un.a_str;
35500Sstevel@tonic-gate 		(void) mdb_walk("umem_cache", (mdb_walk_cb_t)umc_add, &umc);
35510Sstevel@tonic-gate 
35520Sstevel@tonic-gate 		if (umc.umc_nelems == oelems) {
35530Sstevel@tonic-gate 			mdb_warn("unknown umem cache: %s\n", umc.umc_name);
35540Sstevel@tonic-gate 			return (DCMD_ERR);
35550Sstevel@tonic-gate 		}
35560Sstevel@tonic-gate 
35570Sstevel@tonic-gate 		do_all_caches = 0;
35580Sstevel@tonic-gate 		argv++;
35590Sstevel@tonic-gate 		argc--;
35600Sstevel@tonic-gate 	}
35610Sstevel@tonic-gate 
35620Sstevel@tonic-gate 	if (opt_e)
35630Sstevel@tonic-gate 		mem_threshold = cnt_threshold = 0;
35640Sstevel@tonic-gate 
35650Sstevel@tonic-gate 	if (opt_f)
35660Sstevel@tonic-gate 		callback = (mdb_walk_cb_t)umause2;
35670Sstevel@tonic-gate 
35680Sstevel@tonic-gate 	if (do_all_caches) {
35690Sstevel@tonic-gate 		umc.umc_name = NULL; /* match all cache names */
35700Sstevel@tonic-gate 		(void) mdb_walk("umem_cache", (mdb_walk_cb_t)umc_add, &umc);
35710Sstevel@tonic-gate 	}
35720Sstevel@tonic-gate 
35730Sstevel@tonic-gate 	for (i = 0; i < umc.umc_nelems; i++) {
35740Sstevel@tonic-gate 		uintptr_t cp = umc.umc_caches[i];
35750Sstevel@tonic-gate 		umem_cache_t c;
35760Sstevel@tonic-gate 
35770Sstevel@tonic-gate 		if (mdb_vread(&c, sizeof (c), cp) == -1) {
35780Sstevel@tonic-gate 			mdb_warn("failed to read cache at %p", cp);
35790Sstevel@tonic-gate 			continue;
35800Sstevel@tonic-gate 		}
35810Sstevel@tonic-gate 
35820Sstevel@tonic-gate 		if (!(c.cache_flags & UMF_AUDIT)) {
35830Sstevel@tonic-gate 			if (!do_all_caches) {
35840Sstevel@tonic-gate 				mdb_warn("UMF_AUDIT is not enabled for %s\n",
35850Sstevel@tonic-gate 				    c.cache_name);
35860Sstevel@tonic-gate 			}
35870Sstevel@tonic-gate 			continue;
35880Sstevel@tonic-gate 		}
35890Sstevel@tonic-gate 
35900Sstevel@tonic-gate 		umu.umu_cache = &c;
35910Sstevel@tonic-gate 		(void) mdb_pwalk("bufctl", callback, &umu, cp);
35920Sstevel@tonic-gate 		audited_caches++;
35930Sstevel@tonic-gate 	}
35940Sstevel@tonic-gate 
35950Sstevel@tonic-gate 	if (audited_caches == 0 && do_all_caches) {
35960Sstevel@tonic-gate 		mdb_warn("UMF_AUDIT is not enabled for any caches\n");
35970Sstevel@tonic-gate 		return (DCMD_ERR);
35980Sstevel@tonic-gate 	}
35990Sstevel@tonic-gate 
36000Sstevel@tonic-gate 	qsort(umu.umu_hash, umu.umu_nelems, sizeof (umowner_t), umownercmp);
36010Sstevel@tonic-gate 	umoend = umu.umu_hash + umu.umu_nelems;
36020Sstevel@tonic-gate 
36030Sstevel@tonic-gate 	for (umo = umu.umu_hash; umo < umoend; umo++) {
36040Sstevel@tonic-gate 		if (umo->umo_total_size < mem_threshold &&
36050Sstevel@tonic-gate 		    umo->umo_num < cnt_threshold)
36060Sstevel@tonic-gate 			continue;
36070Sstevel@tonic-gate 		mdb_printf("%lu bytes for %u allocations with data size %lu:\n",
36080Sstevel@tonic-gate 		    umo->umo_total_size, umo->umo_num, umo->umo_data_size);
36090Sstevel@tonic-gate 		for (i = 0; i < umo->umo_depth; i++)
36100Sstevel@tonic-gate 			mdb_printf("\t %a\n", umo->umo_stack[i]);
36110Sstevel@tonic-gate 	}
36120Sstevel@tonic-gate 
36130Sstevel@tonic-gate 	return (DCMD_OK);
36140Sstevel@tonic-gate }
3615*1528Sjwadams 
3616*1528Sjwadams struct malloc_data {
3617*1528Sjwadams 	uint32_t malloc_size;
3618*1528Sjwadams 	uint32_t malloc_stat; /* == UMEM_MALLOC_ENCODE(state, malloc_size) */
3619*1528Sjwadams };
3620*1528Sjwadams 
3621*1528Sjwadams #ifdef _LP64
3622*1528Sjwadams #define	UMI_MAX_BUCKET		(UMEM_MAXBUF - 2*sizeof (struct malloc_data))
3623*1528Sjwadams #else
3624*1528Sjwadams #define	UMI_MAX_BUCKET		(UMEM_MAXBUF - sizeof (struct malloc_data))
3625*1528Sjwadams #endif
3626*1528Sjwadams 
3627*1528Sjwadams typedef struct umem_malloc_info {
3628*1528Sjwadams 	size_t um_total;	/* total allocated buffers */
3629*1528Sjwadams 	size_t um_malloc;	/* malloc buffers */
3630*1528Sjwadams 	size_t um_malloc_size;	/* sum of malloc buffer sizes */
3631*1528Sjwadams 	size_t um_malloc_overhead; /* sum of in-chunk overheads */
3632*1528Sjwadams 
3633*1528Sjwadams 	umem_cache_t *um_cp;
3634*1528Sjwadams 
3635*1528Sjwadams 	uint_t *um_bucket;
3636*1528Sjwadams } umem_malloc_info_t;
3637*1528Sjwadams 
3638*1528Sjwadams static const int *
3639*1528Sjwadams dist_linear(int buckets, int beg, int end)
3640*1528Sjwadams {
3641*1528Sjwadams 	int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC);
3642*1528Sjwadams 	int pos;
3643*1528Sjwadams 	int dist = end - beg + 1;
3644*1528Sjwadams 
3645*1528Sjwadams 	for (pos = 0; pos < buckets; pos++)
3646*1528Sjwadams 		out[pos] = beg + (pos * dist)/buckets;
3647*1528Sjwadams 	out[buckets] = end + 1;
3648*1528Sjwadams 
3649*1528Sjwadams 	return (out);
3650*1528Sjwadams }
3651*1528Sjwadams 
3652*1528Sjwadams /*
3653*1528Sjwadams  * We want the bins to be a constant ratio:
3654*1528Sjwadams  *
3655*1528Sjwadams  *	b_0	  = beg;
3656*1528Sjwadams  *	b_idx	  = b_{idx-1} * r;
3657*1528Sjwadams  *	b_buckets = end + 1;
3658*1528Sjwadams  *
3659*1528Sjwadams  * That is:
3660*1528Sjwadams  *
3661*1528Sjwadams  *	       buckets
3662*1528Sjwadams  *	beg * r        = end
3663*1528Sjwadams  *
3664*1528Sjwadams  * Which reduces to:
3665*1528Sjwadams  *
3666*1528Sjwadams  *		  buckets ___________________
3667*1528Sjwadams  *	      r = -------/ ((end + 1) / beg)
3668*1528Sjwadams  *
3669*1528Sjwadams  *		  log ((end + 1) / beg)
3670*1528Sjwadams  *	  log r = ---------------------
3671*1528Sjwadams  *		         buckets
3672*1528Sjwadams  *
3673*1528Sjwadams  *		   (log ((end + 1) / beg)) / buckets
3674*1528Sjwadams  *	      r = e
3675*1528Sjwadams  */
3676*1528Sjwadams static const int *
3677*1528Sjwadams dist_geometric(int buckets, int beg, int end, int minbucketsize)
3678*1528Sjwadams {
3679*1528Sjwadams #ifdef	_KMDB
3680*1528Sjwadams 	return (dist_linear(buckets, beg, end));
3681*1528Sjwadams #else
3682*1528Sjwadams 	int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC);
3683*1528Sjwadams 
3684*1528Sjwadams 	extern double log(double);
3685*1528Sjwadams 	extern double exp(double);
3686*1528Sjwadams 
3687*1528Sjwadams 	double r;
3688*1528Sjwadams 	double b;
3689*1528Sjwadams 	int idx = 0;
3690*1528Sjwadams 	int last;
3691*1528Sjwadams 	int begzero;
3692*1528Sjwadams 
3693*1528Sjwadams 	if (minbucketsize == 0)
3694*1528Sjwadams 		minbucketsize = 1;
3695*1528Sjwadams 
3696*1528Sjwadams 	if (buckets == 1) {
3697*1528Sjwadams 		out[0] = beg;
3698*1528Sjwadams 		out[1] = end + 1;
3699*1528Sjwadams 		return (out);
3700*1528Sjwadams 	}
3701*1528Sjwadams 
3702*1528Sjwadams 	begzero = (beg == 0);
3703*1528Sjwadams 	if (begzero)
3704*1528Sjwadams 		beg = 1;
3705*1528Sjwadams 
3706*1528Sjwadams 	r = exp(log((double)(end + 1) / beg) / buckets);
3707*1528Sjwadams 
3708*1528Sjwadams 	/*
3709*1528Sjwadams 	 * We've now computed r, using the previously derived formula.  We
3710*1528Sjwadams 	 * now need to generate the array of bucket bounds.  There are
3711*1528Sjwadams 	 * two major variables:
3712*1528Sjwadams 	 *
3713*1528Sjwadams 	 *	b	holds b_idx, the current index, as a double.
3714*1528Sjwadams 	 *	last	holds the integer which goes into out[idx]
3715*1528Sjwadams 	 *
3716*1528Sjwadams 	 * Our job is to transform the smooth function b_idx, defined
3717*1528Sjwadams 	 * above, into integer-sized buckets, with a specified minimum
3718*1528Sjwadams 	 * bucket size.  Since b_idx is an exponentially growing function,
3719*1528Sjwadams 	 * any inadequate buckets must be at the beginning.  To deal
3720*1528Sjwadams 	 * with this, we make buckets of minimum size until b catches up
3721*1528Sjwadams 	 * with last.
3722*1528Sjwadams 	 *
3723*1528Sjwadams 	 * A final wrinkle is that beg *can* be zero.  We compute r and b
3724*1528Sjwadams 	 * as if beg was 1, then start last as 0.  This can lead to a bit
3725*1528Sjwadams 	 * of oddness around the 0 bucket, but it's mostly reasonable.
3726*1528Sjwadams 	 */
3727*1528Sjwadams 
3728*1528Sjwadams 	b = last = beg;
3729*1528Sjwadams 	if (begzero)
3730*1528Sjwadams 		last = 0;
3731*1528Sjwadams 
3732*1528Sjwadams 	for (idx = 0; idx < buckets; idx++) {
3733*1528Sjwadams 		int next;
3734*1528Sjwadams 
3735*1528Sjwadams 		out[idx] = last;
3736*1528Sjwadams 
3737*1528Sjwadams 		b *= r;
3738*1528Sjwadams 		next = (int)b;
3739*1528Sjwadams 
3740*1528Sjwadams 		if (next > last + minbucketsize - 1)
3741*1528Sjwadams 			last = next;
3742*1528Sjwadams 		else
3743*1528Sjwadams 			last += minbucketsize;
3744*1528Sjwadams 	}
3745*1528Sjwadams 	out[buckets] = end + 1;
3746*1528Sjwadams 
3747*1528Sjwadams 	return (out);
3748*1528Sjwadams #endif
3749*1528Sjwadams }
3750*1528Sjwadams 
3751*1528Sjwadams #define	NCHARS	50
3752*1528Sjwadams static void
3753*1528Sjwadams umem_malloc_print_dist(uint_t *um_bucket, size_t minmalloc, size_t maxmalloc,
3754*1528Sjwadams     size_t maxbuckets, size_t minbucketsize, int geometric)
3755*1528Sjwadams {
3756*1528Sjwadams 	size_t um_malloc;
3757*1528Sjwadams 	int minb = -1;
3758*1528Sjwadams 	int maxb = -1;
3759*1528Sjwadams 	int buckets;
3760*1528Sjwadams 	int nbucks;
3761*1528Sjwadams 	int i;
3762*1528Sjwadams 	int n;
3763*1528Sjwadams 	int b;
3764*1528Sjwadams 	const char *dist = " Distribution ";
3765*1528Sjwadams 	char dashes[NCHARS + 1];
3766*1528Sjwadams 	const int *distarray;
3767*1528Sjwadams 
3768*1528Sjwadams 	minb = (int)minmalloc;
3769*1528Sjwadams 	maxb = (int)maxmalloc;
3770*1528Sjwadams 
3771*1528Sjwadams 	nbucks = buckets = maxb - minb + 1;
3772*1528Sjwadams 
3773*1528Sjwadams 	um_malloc = 0;
3774*1528Sjwadams 	for (b = minb; b <= maxb; b++)
3775*1528Sjwadams 		um_malloc += um_bucket[b];
3776*1528Sjwadams 	if (um_malloc == 0)
3777*1528Sjwadams 		um_malloc = 1;			/* avoid divide-by-zero */
3778*1528Sjwadams 
3779*1528Sjwadams 	if (maxbuckets != 0)
3780*1528Sjwadams 		buckets = MIN(buckets, maxbuckets);
3781*1528Sjwadams 
3782*1528Sjwadams 	if (minbucketsize > 1) {
3783*1528Sjwadams 		buckets = MIN(buckets, nbucks/minbucketsize);
3784*1528Sjwadams 		if (buckets == 0) {
3785*1528Sjwadams 			buckets = 1;
3786*1528Sjwadams 			minbucketsize = nbucks;
3787*1528Sjwadams 		}
3788*1528Sjwadams 	}
3789*1528Sjwadams 
3790*1528Sjwadams 
3791*1528Sjwadams 	if (geometric)
3792*1528Sjwadams 		distarray = dist_geometric(buckets, minb, maxb, minbucketsize);
3793*1528Sjwadams 	else
3794*1528Sjwadams 		distarray = dist_linear(buckets, minb, maxb);
3795*1528Sjwadams 
3796*1528Sjwadams 	n = (NCHARS - strlen(dist)) / 2;
3797*1528Sjwadams 	(void) memset(dashes, '-', n);
3798*1528Sjwadams 	dashes[n] = 0;
3799*1528Sjwadams 
3800*1528Sjwadams 	mdb_printf("%11s  %s%s%s %s\n",
3801*1528Sjwadams 	    "malloc size", dashes, dist, dashes, "count");
3802*1528Sjwadams 
3803*1528Sjwadams 	for (i = 0; i < buckets; i++) {
3804*1528Sjwadams 		int bb = distarray[i];
3805*1528Sjwadams 		int be = distarray[i+1] - 1;
3806*1528Sjwadams 		uint64_t amount = 0;
3807*1528Sjwadams 
3808*1528Sjwadams 		int nats;
3809*1528Sjwadams 		char ats[NCHARS + 1], spaces[NCHARS + 1];
3810*1528Sjwadams 		char range[40];
3811*1528Sjwadams 
3812*1528Sjwadams 		for (b = bb; b <= be; b++)
3813*1528Sjwadams 			amount += um_bucket[b];
3814*1528Sjwadams 
3815*1528Sjwadams 		nats = (NCHARS * amount)/um_malloc;
3816*1528Sjwadams 		(void) memset(ats, '@', nats);
3817*1528Sjwadams 		ats[nats] = 0;
3818*1528Sjwadams 		(void) memset(spaces, ' ', NCHARS - nats);
3819*1528Sjwadams 		spaces[NCHARS - nats] = 0;
3820*1528Sjwadams 
3821*1528Sjwadams 		if (bb == be)
3822*1528Sjwadams 			mdb_snprintf(range, sizeof (range), "%d", bb);
3823*1528Sjwadams 		else
3824*1528Sjwadams 			mdb_snprintf(range, sizeof (range), "%d-%d", bb, be);
3825*1528Sjwadams 		mdb_printf("%11s |%s%s %lld\n", range, ats, spaces, amount);
3826*1528Sjwadams 	}
3827*1528Sjwadams 	mdb_printf("\n");
3828*1528Sjwadams }
3829*1528Sjwadams #undef NCHARS
3830*1528Sjwadams 
3831*1528Sjwadams /*
3832*1528Sjwadams  * A malloc()ed buffer looks like:
3833*1528Sjwadams  *
3834*1528Sjwadams  *	<----------- mi.malloc_size --->
3835*1528Sjwadams  *	<----------- cp.cache_bufsize ------------------>
3836*1528Sjwadams  *	<----------- cp.cache_chunksize -------------------------------->
3837*1528Sjwadams  *	+-------+-----------------------+---------------+---------------+
3838*1528Sjwadams  *	|/tag///| mallocsz		|/round-off/////|/debug info////|
3839*1528Sjwadams  *	+-------+---------------------------------------+---------------+
3840*1528Sjwadams  *		<-- usable space ------>
3841*1528Sjwadams  *
3842*1528Sjwadams  * mallocsz is the argument to malloc(3C).
3843*1528Sjwadams  * mi.malloc_size is the actual size passed to umem_alloc(), which
3844*1528Sjwadams  * is rounded up to the smallest available cache size, which is
3845*1528Sjwadams  * cache_bufsize.  If there is debugging or alignment overhead in
3846*1528Sjwadams  * the cache, that is reflected in a larger cache_chunksize.
3847*1528Sjwadams  *
3848*1528Sjwadams  * The tag at the beginning of the buffer is either 8-bytes or 16-bytes,
3849*1528Sjwadams  * depending upon the ISA's alignment requirements.  For 32-bit allocations,
3850*1528Sjwadams  * it is always a 8-byte tag.  For 64-bit allocations larger than 8 bytes,
3851*1528Sjwadams  * the tag has 8 bytes of padding before it.
3852*1528Sjwadams  *
3853*1528Sjwadams  * 32-byte, 64-byte buffers <= 8 bytes:
3854*1528Sjwadams  *	+-------+-------+--------- ...
3855*1528Sjwadams  *	|/size//|/stat//| mallocsz ...
3856*1528Sjwadams  *	+-------+-------+--------- ...
3857*1528Sjwadams  *			^
3858*1528Sjwadams  *			pointer returned from malloc(3C)
3859*1528Sjwadams  *
3860*1528Sjwadams  * 64-byte buffers > 8 bytes:
3861*1528Sjwadams  *	+---------------+-------+-------+--------- ...
3862*1528Sjwadams  *	|/padding///////|/size//|/stat//| mallocsz ...
3863*1528Sjwadams  *	+---------------+-------+-------+--------- ...
3864*1528Sjwadams  *					^
3865*1528Sjwadams  *					pointer returned from malloc(3C)
3866*1528Sjwadams  *
3867*1528Sjwadams  * The "size" field is "malloc_size", which is mallocsz + the padding.
3868*1528Sjwadams  * The "stat" field is derived from malloc_size, and functions as a
3869*1528Sjwadams  * validation that this buffer is actually from malloc(3C).
3870*1528Sjwadams  */
3871*1528Sjwadams /*ARGSUSED*/
3872*1528Sjwadams static int
3873*1528Sjwadams um_umem_buffer_cb(uintptr_t addr, void *buf, umem_malloc_info_t *ump)
3874*1528Sjwadams {
3875*1528Sjwadams 	struct malloc_data md;
3876*1528Sjwadams 	size_t m_addr = addr;
3877*1528Sjwadams 	size_t overhead = sizeof (md);
3878*1528Sjwadams 	size_t mallocsz;
3879*1528Sjwadams 
3880*1528Sjwadams 	ump->um_total++;
3881*1528Sjwadams 
3882*1528Sjwadams #ifdef _LP64
3883*1528Sjwadams 	if (ump->um_cp->cache_bufsize > UMEM_SECOND_ALIGN) {
3884*1528Sjwadams 		m_addr += overhead;
3885*1528Sjwadams 		overhead += sizeof (md);
3886*1528Sjwadams 	}
3887*1528Sjwadams #endif
3888*1528Sjwadams 
3889*1528Sjwadams 	if (mdb_vread(&md, sizeof (md), m_addr) == -1) {
3890*1528Sjwadams 		mdb_warn("unable to read malloc header at %p", m_addr);
3891*1528Sjwadams 		return (WALK_NEXT);
3892*1528Sjwadams 	}
3893*1528Sjwadams 
3894*1528Sjwadams 	switch (UMEM_MALLOC_DECODE(md.malloc_stat, md.malloc_size)) {
3895*1528Sjwadams 	case MALLOC_MAGIC:
3896*1528Sjwadams #ifdef _LP64
3897*1528Sjwadams 	case MALLOC_SECOND_MAGIC:
3898*1528Sjwadams #endif
3899*1528Sjwadams 		mallocsz = md.malloc_size - overhead;
3900*1528Sjwadams 
3901*1528Sjwadams 		ump->um_malloc++;
3902*1528Sjwadams 		ump->um_malloc_size += mallocsz;
3903*1528Sjwadams 		ump->um_malloc_overhead += overhead;
3904*1528Sjwadams 
3905*1528Sjwadams 		/* include round-off and debug overhead */
3906*1528Sjwadams 		ump->um_malloc_overhead +=
3907*1528Sjwadams 		    ump->um_cp->cache_chunksize - md.malloc_size;
3908*1528Sjwadams 
3909*1528Sjwadams 		if (ump->um_bucket != NULL && mallocsz <= UMI_MAX_BUCKET)
3910*1528Sjwadams 			ump->um_bucket[mallocsz]++;
3911*1528Sjwadams 
3912*1528Sjwadams 		break;
3913*1528Sjwadams 	default:
3914*1528Sjwadams 		break;
3915*1528Sjwadams 	}
3916*1528Sjwadams 
3917*1528Sjwadams 	return (WALK_NEXT);
3918*1528Sjwadams }
3919*1528Sjwadams 
3920*1528Sjwadams int
3921*1528Sjwadams get_umem_alloc_sizes(int **out, size_t *out_num)
3922*1528Sjwadams {
3923*1528Sjwadams 	GElf_Sym sym;
3924*1528Sjwadams 
3925*1528Sjwadams 	if (umem_lookup_by_name("umem_alloc_sizes", &sym) == -1) {
3926*1528Sjwadams 		mdb_warn("unable to look up umem_alloc_sizes");
3927*1528Sjwadams 		return (-1);
3928*1528Sjwadams 	}
3929*1528Sjwadams 
3930*1528Sjwadams 	*out = mdb_alloc(sym.st_size, UM_SLEEP | UM_GC);
3931*1528Sjwadams 	*out_num = sym.st_size / sizeof (int);
3932*1528Sjwadams 
3933*1528Sjwadams 	if (mdb_vread(*out, sym.st_size, sym.st_value) == -1) {
3934*1528Sjwadams 		mdb_warn("unable to read umem_alloc_sizes (%p)", sym.st_value);
3935*1528Sjwadams 		*out = NULL;
3936*1528Sjwadams 		return (-1);
3937*1528Sjwadams 	}
3938*1528Sjwadams 
3939*1528Sjwadams 	return (0);
3940*1528Sjwadams }
3941*1528Sjwadams 
3942*1528Sjwadams 
3943*1528Sjwadams static int
3944*1528Sjwadams um_umem_cache_cb(uintptr_t addr, umem_cache_t *cp, umem_malloc_info_t *ump)
3945*1528Sjwadams {
3946*1528Sjwadams 	if (strncmp(cp->cache_name, "umem_alloc_", strlen("umem_alloc_")) != 0)
3947*1528Sjwadams 		return (WALK_NEXT);
3948*1528Sjwadams 
3949*1528Sjwadams 	ump->um_cp = cp;
3950*1528Sjwadams 
3951*1528Sjwadams 	if (mdb_pwalk("umem", (mdb_walk_cb_t)um_umem_buffer_cb, ump, addr) ==
3952*1528Sjwadams 	    -1) {
3953*1528Sjwadams 		mdb_warn("can't walk 'umem' for cache %p", addr);
3954*1528Sjwadams 		return (WALK_ERR);
3955*1528Sjwadams 	}
3956*1528Sjwadams 
3957*1528Sjwadams 	return (WALK_NEXT);
3958*1528Sjwadams }
3959*1528Sjwadams 
3960*1528Sjwadams void
3961*1528Sjwadams umem_malloc_dist_help(void)
3962*1528Sjwadams {
3963*1528Sjwadams 	mdb_printf("%s\n",
3964*1528Sjwadams 	    "report distribution of outstanding malloc()s");
3965*1528Sjwadams 	mdb_dec_indent(2);
3966*1528Sjwadams 	mdb_printf("%<b>OPTIONS%</b>\n");
3967*1528Sjwadams 	mdb_inc_indent(2);
3968*1528Sjwadams 	mdb_printf("%s",
3969*1528Sjwadams "  -b maxbins\n"
3970*1528Sjwadams "        Use at most maxbins bins for the data\n"
3971*1528Sjwadams "  -B minbinsize\n"
3972*1528Sjwadams "        Make the bins at least minbinsize bytes apart\n"
3973*1528Sjwadams "  -d    dump the raw data out, without binning\n"
3974*1528Sjwadams "  -g    use geometric binning instead of linear binning\n");
3975*1528Sjwadams }
3976*1528Sjwadams 
3977*1528Sjwadams /*ARGSUSED*/
3978*1528Sjwadams int
3979*1528Sjwadams umem_malloc_dist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3980*1528Sjwadams {
3981*1528Sjwadams 	umem_malloc_info_t mi;
3982*1528Sjwadams 	uint_t geometric = 0;
3983*1528Sjwadams 	uint_t dump = 0;
3984*1528Sjwadams 	size_t maxbuckets = 0;
3985*1528Sjwadams 	size_t minbucketsize = 0;
3986*1528Sjwadams 
3987*1528Sjwadams 	size_t minalloc = 0;
3988*1528Sjwadams 	size_t maxalloc = UMI_MAX_BUCKET;
3989*1528Sjwadams 
3990*1528Sjwadams 	if (flags & DCMD_ADDRSPEC)
3991*1528Sjwadams 		return (DCMD_USAGE);
3992*1528Sjwadams 
3993*1528Sjwadams 	if (mdb_getopts(argc, argv,
3994*1528Sjwadams 	    'd', MDB_OPT_SETBITS, TRUE, &dump,
3995*1528Sjwadams 	    'g', MDB_OPT_SETBITS, TRUE, &geometric,
3996*1528Sjwadams 	    'b', MDB_OPT_UINTPTR, &maxbuckets,
3997*1528Sjwadams 	    'B', MDB_OPT_UINTPTR, &minbucketsize,
3998*1528Sjwadams 	    0) != argc)
3999*1528Sjwadams 		return (DCMD_USAGE);
4000*1528Sjwadams 
4001*1528Sjwadams 	bzero(&mi, sizeof (mi));
4002*1528Sjwadams 	mi.um_bucket = mdb_zalloc((UMI_MAX_BUCKET + 1) * sizeof (*mi.um_bucket),
4003*1528Sjwadams 	    UM_SLEEP | UM_GC);
4004*1528Sjwadams 
4005*1528Sjwadams 	if (mdb_walk("umem_cache", (mdb_walk_cb_t)um_umem_cache_cb,
4006*1528Sjwadams 	    &mi) == -1) {
4007*1528Sjwadams 		mdb_warn("unable to walk 'umem_cache'");
4008*1528Sjwadams 		return (DCMD_ERR);
4009*1528Sjwadams 	}
4010*1528Sjwadams 
4011*1528Sjwadams 	if (dump) {
4012*1528Sjwadams 		int i;
4013*1528Sjwadams 		for (i = minalloc; i <= maxalloc; i++)
4014*1528Sjwadams 			mdb_printf("%d\t%d\n", i, mi.um_bucket[i]);
4015*1528Sjwadams 
4016*1528Sjwadams 		return (DCMD_OK);
4017*1528Sjwadams 	}
4018*1528Sjwadams 
4019*1528Sjwadams 	umem_malloc_print_dist(mi.um_bucket, minalloc, maxalloc,
4020*1528Sjwadams 	    maxbuckets, minbucketsize, geometric);
4021*1528Sjwadams 
4022*1528Sjwadams 	return (DCMD_OK);
4023*1528Sjwadams }
4024*1528Sjwadams 
4025*1528Sjwadams void
4026*1528Sjwadams umem_malloc_info_help(void)
4027*1528Sjwadams {
4028*1528Sjwadams 	mdb_printf("%s\n",
4029*1528Sjwadams 	    "report information about malloc()s by cache.  ");
4030*1528Sjwadams 	mdb_dec_indent(2);
4031*1528Sjwadams 	mdb_printf("%<b>OPTIONS%</b>\n");
4032*1528Sjwadams 	mdb_inc_indent(2);
4033*1528Sjwadams 	mdb_printf("%s",
4034*1528Sjwadams "  -b maxbins\n"
4035*1528Sjwadams "        Use at most maxbins bins for the data\n"
4036*1528Sjwadams "  -B minbinsize\n"
4037*1528Sjwadams "        Make the bins at least minbinsize bytes apart\n"
4038*1528Sjwadams "  -d    dump the raw distribution data without binning\n"
4039*1528Sjwadams #ifndef _KMDB
4040*1528Sjwadams "  -g    use geometric binning instead of linear binning\n"
4041*1528Sjwadams #endif
4042*1528Sjwadams 	    "");
4043*1528Sjwadams }
4044*1528Sjwadams int
4045*1528Sjwadams umem_malloc_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
4046*1528Sjwadams {
4047*1528Sjwadams 	umem_cache_t c;
4048*1528Sjwadams 	umem_malloc_info_t mi;
4049*1528Sjwadams 
4050*1528Sjwadams 	int skip = 0;
4051*1528Sjwadams 
4052*1528Sjwadams 	size_t maxmalloc;
4053*1528Sjwadams 	size_t overhead;
4054*1528Sjwadams 	size_t allocated;
4055*1528Sjwadams 	size_t avg_malloc;
4056*1528Sjwadams 	size_t overhead_pct;	/* 1000 * overhead_percent */
4057*1528Sjwadams 
4058*1528Sjwadams 	uint_t verbose = 0;
4059*1528Sjwadams 	uint_t dump = 0;
4060*1528Sjwadams 	uint_t geometric = 0;
4061*1528Sjwadams 	size_t maxbuckets = 0;
4062*1528Sjwadams 	size_t minbucketsize = 0;
4063*1528Sjwadams 
4064*1528Sjwadams 	int *alloc_sizes;
4065*1528Sjwadams 	int idx;
4066*1528Sjwadams 	size_t num;
4067*1528Sjwadams 	size_t minmalloc;
4068*1528Sjwadams 
4069*1528Sjwadams 	if (mdb_getopts(argc, argv,
4070*1528Sjwadams 	    'd', MDB_OPT_SETBITS, TRUE, &dump,
4071*1528Sjwadams 	    'g', MDB_OPT_SETBITS, TRUE, &geometric,
4072*1528Sjwadams 	    'b', MDB_OPT_UINTPTR, &maxbuckets,
4073*1528Sjwadams 	    'B', MDB_OPT_UINTPTR, &minbucketsize,
4074*1528Sjwadams 	    0) != argc)
4075*1528Sjwadams 		return (DCMD_USAGE);
4076*1528Sjwadams 
4077*1528Sjwadams 	if (dump || geometric || (maxbuckets != 0) || (minbucketsize != 0))
4078*1528Sjwadams 		verbose = 1;
4079*1528Sjwadams 
4080*1528Sjwadams 	if (!(flags & DCMD_ADDRSPEC)) {
4081*1528Sjwadams 		if (mdb_walk_dcmd("umem_cache", "umem_malloc_info",
4082*1528Sjwadams 		    argc, argv) == -1) {
4083*1528Sjwadams 			mdb_warn("can't walk umem_cache");
4084*1528Sjwadams 			return (DCMD_ERR);
4085*1528Sjwadams 		}
4086*1528Sjwadams 		return (DCMD_OK);
4087*1528Sjwadams 	}
4088*1528Sjwadams 
4089*1528Sjwadams 	if (!mdb_vread(&c, sizeof (c), addr)) {
4090*1528Sjwadams 		mdb_warn("unable to read cache at %p", addr);
4091*1528Sjwadams 		return (DCMD_ERR);
4092*1528Sjwadams 	}
4093*1528Sjwadams 
4094*1528Sjwadams 	if (strncmp(c.cache_name, "umem_alloc_", strlen("umem_alloc_")) != 0) {
4095*1528Sjwadams 		if (!(flags & DCMD_LOOP))
4096*1528Sjwadams 			mdb_warn("umem_malloc_info: cache \"%s\" is not used "
4097*1528Sjwadams 			    "by malloc()\n", c.cache_name);
4098*1528Sjwadams 		skip = 1;
4099*1528Sjwadams 	}
4100*1528Sjwadams 
4101*1528Sjwadams 	/*
4102*1528Sjwadams 	 * normally, print the header only the first time.  In verbose mode,
4103*1528Sjwadams 	 * print the header on every non-skipped buffer
4104*1528Sjwadams 	 */
4105*1528Sjwadams 	if ((!verbose && DCMD_HDRSPEC(flags)) || (verbose && !skip))
4106*1528Sjwadams 		mdb_printf("%<ul>%-?s %6s %6s %8s %8s %10s %10s %6s%</ul>\n",
4107*1528Sjwadams 		    "CACHE", "BUFSZ", "MAXMAL",
4108*1528Sjwadams 		    "BUFMALLC", "AVG_MAL", "MALLOCED", "OVERHEAD", "%OVER");
4109*1528Sjwadams 
4110*1528Sjwadams 	if (skip)
4111*1528Sjwadams 		return (DCMD_OK);
4112*1528Sjwadams 
4113*1528Sjwadams 	maxmalloc = c.cache_bufsize - sizeof (struct malloc_data);
4114*1528Sjwadams #ifdef _LP64
4115*1528Sjwadams 	if (c.cache_bufsize > UMEM_SECOND_ALIGN)
4116*1528Sjwadams 		maxmalloc -= sizeof (struct malloc_data);
4117*1528Sjwadams #endif
4118*1528Sjwadams 
4119*1528Sjwadams 	bzero(&mi, sizeof (mi));
4120*1528Sjwadams 	mi.um_cp = &c;
4121*1528Sjwadams 	if (verbose)
4122*1528Sjwadams 		mi.um_bucket =
4123*1528Sjwadams 		    mdb_zalloc((UMI_MAX_BUCKET + 1) * sizeof (*mi.um_bucket),
4124*1528Sjwadams 		    UM_SLEEP | UM_GC);
4125*1528Sjwadams 
4126*1528Sjwadams 	if (mdb_pwalk("umem", (mdb_walk_cb_t)um_umem_buffer_cb, &mi, addr) ==
4127*1528Sjwadams 	    -1) {
4128*1528Sjwadams 		mdb_warn("can't walk 'umem'");
4129*1528Sjwadams 		return (DCMD_ERR);
4130*1528Sjwadams 	}
4131*1528Sjwadams 
4132*1528Sjwadams 	overhead = mi.um_malloc_overhead;
4133*1528Sjwadams 	allocated = mi.um_malloc_size;
4134*1528Sjwadams 
4135*1528Sjwadams 	/* do integer round off for the average */
4136*1528Sjwadams 	if (mi.um_malloc != 0)
4137*1528Sjwadams 		avg_malloc = (allocated + (mi.um_malloc - 1)/2) / mi.um_malloc;
4138*1528Sjwadams 	else
4139*1528Sjwadams 		avg_malloc = 0;
4140*1528Sjwadams 
4141*1528Sjwadams 	/*
4142*1528Sjwadams 	 * include per-slab overhead
4143*1528Sjwadams 	 *
4144*1528Sjwadams 	 * Each slab in a given cache is the same size, and has the same
4145*1528Sjwadams 	 * number of chunks in it;  we read in the first slab on the
4146*1528Sjwadams 	 * slab list to get the number of chunks for all slabs.  To
4147*1528Sjwadams 	 * compute the per-slab overhead, we just subtract the chunk usage
4148*1528Sjwadams 	 * from the slabsize:
4149*1528Sjwadams 	 *
4150*1528Sjwadams 	 * +------------+-------+-------+ ... --+-------+-------+-------+
4151*1528Sjwadams 	 * |////////////|	|	| ...	|	|///////|///////|
4152*1528Sjwadams 	 * |////color///| chunk	| chunk	| ...	| chunk	|/color/|/slab//|
4153*1528Sjwadams 	 * |////////////|	|	| ...	|	|///////|///////|
4154*1528Sjwadams 	 * +------------+-------+-------+ ... --+-------+-------+-------+
4155*1528Sjwadams 	 * |		\_______chunksize * chunks_____/		|
4156*1528Sjwadams 	 * \__________________________slabsize__________________________/
4157*1528Sjwadams 	 *
4158*1528Sjwadams 	 * For UMF_HASH caches, there is an additional source of overhead;
4159*1528Sjwadams 	 * the external umem_slab_t and per-chunk bufctl structures.  We
4160*1528Sjwadams 	 * include those in our per-slab overhead.
4161*1528Sjwadams 	 *
4162*1528Sjwadams 	 * Once we have a number for the per-slab overhead, we estimate
4163*1528Sjwadams 	 * the actual overhead by treating the malloc()ed buffers as if
4164*1528Sjwadams 	 * they were densely packed:
4165*1528Sjwadams 	 *
4166*1528Sjwadams 	 *	additional overhead = (# mallocs) * (per-slab) / (chunks);
4167*1528Sjwadams 	 *
4168*1528Sjwadams 	 * carefully ordering the multiply before the divide, to avoid
4169*1528Sjwadams 	 * round-off error.
4170*1528Sjwadams 	 */
4171*1528Sjwadams 	if (mi.um_malloc != 0) {
4172*1528Sjwadams 		umem_slab_t slab;
4173*1528Sjwadams 		uintptr_t saddr = (uintptr_t)c.cache_nullslab.slab_next;
4174*1528Sjwadams 
4175*1528Sjwadams 		if (mdb_vread(&slab, sizeof (slab), saddr) == -1) {
4176*1528Sjwadams 			mdb_warn("unable to read slab at %p\n", saddr);
4177*1528Sjwadams 		} else {
4178*1528Sjwadams 			long chunks = slab.slab_chunks;
4179*1528Sjwadams 			if (chunks != 0 && c.cache_chunksize != 0 &&
4180*1528Sjwadams 			    chunks <= c.cache_slabsize / c.cache_chunksize) {
4181*1528Sjwadams 				uintmax_t perslab =
4182*1528Sjwadams 				    c.cache_slabsize -
4183*1528Sjwadams 				    (c.cache_chunksize * chunks);
4184*1528Sjwadams 
4185*1528Sjwadams 				if (c.cache_flags & UMF_HASH) {
4186*1528Sjwadams 					perslab += sizeof (umem_slab_t) +
4187*1528Sjwadams 					    chunks *
4188*1528Sjwadams 					    ((c.cache_flags & UMF_AUDIT) ?
4189*1528Sjwadams 					    sizeof (umem_bufctl_audit_t) :
4190*1528Sjwadams 					    sizeof (umem_bufctl_t));
4191*1528Sjwadams 				}
4192*1528Sjwadams 				overhead +=
4193*1528Sjwadams 				    (perslab * (uintmax_t)mi.um_malloc)/chunks;
4194*1528Sjwadams 			} else {
4195*1528Sjwadams 				mdb_warn("invalid #chunks (%d) in slab %p\n",
4196*1528Sjwadams 				    chunks, saddr);
4197*1528Sjwadams 			}
4198*1528Sjwadams 		}
4199*1528Sjwadams 	}
4200*1528Sjwadams 
4201*1528Sjwadams 	if (allocated != 0)
4202*1528Sjwadams 		overhead_pct = (1000ULL * overhead) / allocated;
4203*1528Sjwadams 	else
4204*1528Sjwadams 		overhead_pct = 0;
4205*1528Sjwadams 
4206*1528Sjwadams 	mdb_printf("%0?p %6ld %6ld %8ld %8ld %10ld %10ld %3ld.%01ld%%\n",
4207*1528Sjwadams 	    addr, c.cache_bufsize, maxmalloc,
4208*1528Sjwadams 	    mi.um_malloc, avg_malloc, allocated, overhead,
4209*1528Sjwadams 	    overhead_pct / 10, overhead_pct % 10);
4210*1528Sjwadams 
4211*1528Sjwadams 	if (!verbose)
4212*1528Sjwadams 		return (DCMD_OK);
4213*1528Sjwadams 
4214*1528Sjwadams 	if (!dump)
4215*1528Sjwadams 		mdb_printf("\n");
4216*1528Sjwadams 
4217*1528Sjwadams 	if (get_umem_alloc_sizes(&alloc_sizes, &num) == -1)
4218*1528Sjwadams 		return (DCMD_ERR);
4219*1528Sjwadams 
4220*1528Sjwadams 	for (idx = 0; idx < num; idx++) {
4221*1528Sjwadams 		if (alloc_sizes[idx] == c.cache_bufsize)
4222*1528Sjwadams 			break;
4223*1528Sjwadams 		if (alloc_sizes[idx] == 0) {
4224*1528Sjwadams 			idx = num;	/* 0-terminated array */
4225*1528Sjwadams 			break;
4226*1528Sjwadams 		}
4227*1528Sjwadams 	}
4228*1528Sjwadams 	if (idx == num) {
4229*1528Sjwadams 		mdb_warn(
4230*1528Sjwadams 		    "cache %p's size (%d) not in umem_alloc_sizes\n",
4231*1528Sjwadams 		    addr, c.cache_bufsize);
4232*1528Sjwadams 		return (DCMD_ERR);
4233*1528Sjwadams 	}
4234*1528Sjwadams 
4235*1528Sjwadams 	minmalloc = (idx == 0)? 0 : alloc_sizes[idx - 1];
4236*1528Sjwadams 	if (minmalloc > 0) {
4237*1528Sjwadams #ifdef _LP64
4238*1528Sjwadams 		if (minmalloc > UMEM_SECOND_ALIGN)
4239*1528Sjwadams 			minmalloc -= sizeof (struct malloc_data);
4240*1528Sjwadams #endif
4241*1528Sjwadams 		minmalloc -= sizeof (struct malloc_data);
4242*1528Sjwadams 		minmalloc += 1;
4243*1528Sjwadams 	}
4244*1528Sjwadams 
4245*1528Sjwadams 	if (dump) {
4246*1528Sjwadams 		for (idx = minmalloc; idx <= maxmalloc; idx++)
4247*1528Sjwadams 			mdb_printf("%d\t%d\n", idx, mi.um_bucket[idx]);
4248*1528Sjwadams 		mdb_printf("\n");
4249*1528Sjwadams 	} else {
4250*1528Sjwadams 		umem_malloc_print_dist(mi.um_bucket, minmalloc, maxmalloc,
4251*1528Sjwadams 		    maxbuckets, minbucketsize, geometric);
4252*1528Sjwadams 	}
4253*1528Sjwadams 
4254*1528Sjwadams 	return (DCMD_OK);
4255*1528Sjwadams }
4256