xref: /onnv-gate/usr/src/cmd/mdb/common/modules/genunix/kmem.c (revision 4798:8e0fa896ea0b)
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
51528Sjwadams  * Common Development and Distribution License (the "License").
61528Sjwadams  * 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 /*
224688Stomee  * Copyright 2007 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 <mdb/mdb_param.h>
290Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
300Sstevel@tonic-gate #include <mdb/mdb_ctf.h>
310Sstevel@tonic-gate #include <sys/cpuvar.h>
320Sstevel@tonic-gate #include <sys/kmem_impl.h>
330Sstevel@tonic-gate #include <sys/vmem_impl.h>
340Sstevel@tonic-gate #include <sys/machelf.h>
350Sstevel@tonic-gate #include <sys/modctl.h>
360Sstevel@tonic-gate #include <sys/kobj.h>
370Sstevel@tonic-gate #include <sys/panic.h>
380Sstevel@tonic-gate #include <sys/stack.h>
390Sstevel@tonic-gate #include <sys/sysmacros.h>
400Sstevel@tonic-gate #include <vm/page.h>
410Sstevel@tonic-gate 
42*4798Stomee #include "dist.h"
430Sstevel@tonic-gate #include "kmem.h"
441528Sjwadams #include "leaky.h"
450Sstevel@tonic-gate 
460Sstevel@tonic-gate #define	dprintf(x) if (mdb_debug_level) { \
470Sstevel@tonic-gate 	mdb_printf("kmem debug: ");  \
480Sstevel@tonic-gate 	/*CSTYLED*/\
490Sstevel@tonic-gate 	mdb_printf x ;\
500Sstevel@tonic-gate }
510Sstevel@tonic-gate 
520Sstevel@tonic-gate #define	KM_ALLOCATED		0x01
530Sstevel@tonic-gate #define	KM_FREE			0x02
540Sstevel@tonic-gate #define	KM_BUFCTL		0x04
550Sstevel@tonic-gate #define	KM_CONSTRUCTED		0x08	/* only constructed free buffers */
560Sstevel@tonic-gate #define	KM_HASH			0x10
570Sstevel@tonic-gate 
580Sstevel@tonic-gate static int mdb_debug_level = 0;
590Sstevel@tonic-gate 
600Sstevel@tonic-gate /*ARGSUSED*/
610Sstevel@tonic-gate static int
620Sstevel@tonic-gate kmem_init_walkers(uintptr_t addr, const kmem_cache_t *c, void *ignored)
630Sstevel@tonic-gate {
640Sstevel@tonic-gate 	mdb_walker_t w;
650Sstevel@tonic-gate 	char descr[64];
660Sstevel@tonic-gate 
670Sstevel@tonic-gate 	(void) mdb_snprintf(descr, sizeof (descr),
680Sstevel@tonic-gate 	    "walk the %s cache", c->cache_name);
690Sstevel@tonic-gate 
700Sstevel@tonic-gate 	w.walk_name = c->cache_name;
710Sstevel@tonic-gate 	w.walk_descr = descr;
720Sstevel@tonic-gate 	w.walk_init = kmem_walk_init;
730Sstevel@tonic-gate 	w.walk_step = kmem_walk_step;
740Sstevel@tonic-gate 	w.walk_fini = kmem_walk_fini;
750Sstevel@tonic-gate 	w.walk_init_arg = (void *)addr;
760Sstevel@tonic-gate 
770Sstevel@tonic-gate 	if (mdb_add_walker(&w) == -1)
780Sstevel@tonic-gate 		mdb_warn("failed to add %s walker", c->cache_name);
790Sstevel@tonic-gate 
800Sstevel@tonic-gate 	return (WALK_NEXT);
810Sstevel@tonic-gate }
820Sstevel@tonic-gate 
830Sstevel@tonic-gate /*ARGSUSED*/
840Sstevel@tonic-gate int
850Sstevel@tonic-gate kmem_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
860Sstevel@tonic-gate {
870Sstevel@tonic-gate 	mdb_debug_level ^= 1;
880Sstevel@tonic-gate 
890Sstevel@tonic-gate 	mdb_printf("kmem: debugging is now %s\n",
900Sstevel@tonic-gate 	    mdb_debug_level ? "on" : "off");
910Sstevel@tonic-gate 
920Sstevel@tonic-gate 	return (DCMD_OK);
930Sstevel@tonic-gate }
940Sstevel@tonic-gate 
950Sstevel@tonic-gate typedef struct {
960Sstevel@tonic-gate 	uintptr_t kcw_first;
970Sstevel@tonic-gate 	uintptr_t kcw_current;
980Sstevel@tonic-gate } kmem_cache_walk_t;
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate int
1010Sstevel@tonic-gate kmem_cache_walk_init(mdb_walk_state_t *wsp)
1020Sstevel@tonic-gate {
1030Sstevel@tonic-gate 	kmem_cache_walk_t *kcw;
1040Sstevel@tonic-gate 	kmem_cache_t c;
1050Sstevel@tonic-gate 	uintptr_t cp;
1060Sstevel@tonic-gate 	GElf_Sym sym;
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate 	if (mdb_lookup_by_name("kmem_null_cache", &sym) == -1) {
1090Sstevel@tonic-gate 		mdb_warn("couldn't find kmem_null_cache");
1100Sstevel@tonic-gate 		return (WALK_ERR);
1110Sstevel@tonic-gate 	}
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 	cp = (uintptr_t)sym.st_value;
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (kmem_cache_t), cp) == -1) {
1160Sstevel@tonic-gate 		mdb_warn("couldn't read cache at %p", cp);
1170Sstevel@tonic-gate 		return (WALK_ERR);
1180Sstevel@tonic-gate 	}
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 	kcw = mdb_alloc(sizeof (kmem_cache_walk_t), UM_SLEEP);
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 	kcw->kcw_first = cp;
1230Sstevel@tonic-gate 	kcw->kcw_current = (uintptr_t)c.cache_next;
1240Sstevel@tonic-gate 	wsp->walk_data = kcw;
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 	return (WALK_NEXT);
1270Sstevel@tonic-gate }
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate int
1300Sstevel@tonic-gate kmem_cache_walk_step(mdb_walk_state_t *wsp)
1310Sstevel@tonic-gate {
1320Sstevel@tonic-gate 	kmem_cache_walk_t *kcw = wsp->walk_data;
1330Sstevel@tonic-gate 	kmem_cache_t c;
1340Sstevel@tonic-gate 	int status;
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (kmem_cache_t), kcw->kcw_current) == -1) {
1370Sstevel@tonic-gate 		mdb_warn("couldn't read cache at %p", kcw->kcw_current);
1380Sstevel@tonic-gate 		return (WALK_DONE);
1390Sstevel@tonic-gate 	}
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	status = wsp->walk_callback(kcw->kcw_current, &c, wsp->walk_cbdata);
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	if ((kcw->kcw_current = (uintptr_t)c.cache_next) == kcw->kcw_first)
1440Sstevel@tonic-gate 		return (WALK_DONE);
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	return (status);
1470Sstevel@tonic-gate }
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate void
1500Sstevel@tonic-gate kmem_cache_walk_fini(mdb_walk_state_t *wsp)
1510Sstevel@tonic-gate {
1520Sstevel@tonic-gate 	kmem_cache_walk_t *kcw = wsp->walk_data;
1530Sstevel@tonic-gate 	mdb_free(kcw, sizeof (kmem_cache_walk_t));
1540Sstevel@tonic-gate }
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate int
1570Sstevel@tonic-gate kmem_cpu_cache_walk_init(mdb_walk_state_t *wsp)
1580Sstevel@tonic-gate {
1590Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
1600Sstevel@tonic-gate 		mdb_warn("kmem_cpu_cache doesn't support global walks");
1610Sstevel@tonic-gate 		return (WALK_ERR);
1620Sstevel@tonic-gate 	}
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	if (mdb_layered_walk("cpu", wsp) == -1) {
1650Sstevel@tonic-gate 		mdb_warn("couldn't walk 'cpu'");
1660Sstevel@tonic-gate 		return (WALK_ERR);
1670Sstevel@tonic-gate 	}
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate 	wsp->walk_data = (void *)wsp->walk_addr;
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 	return (WALK_NEXT);
1720Sstevel@tonic-gate }
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate int
1750Sstevel@tonic-gate kmem_cpu_cache_walk_step(mdb_walk_state_t *wsp)
1760Sstevel@tonic-gate {
1770Sstevel@tonic-gate 	uintptr_t caddr = (uintptr_t)wsp->walk_data;
1780Sstevel@tonic-gate 	const cpu_t *cpu = wsp->walk_layer;
1790Sstevel@tonic-gate 	kmem_cpu_cache_t cc;
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 	caddr += cpu->cpu_cache_offset;
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 	if (mdb_vread(&cc, sizeof (kmem_cpu_cache_t), caddr) == -1) {
1840Sstevel@tonic-gate 		mdb_warn("couldn't read kmem_cpu_cache at %p", caddr);
1850Sstevel@tonic-gate 		return (WALK_ERR);
1860Sstevel@tonic-gate 	}
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	return (wsp->walk_callback(caddr, &cc, wsp->walk_cbdata));
1890Sstevel@tonic-gate }
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate int
1920Sstevel@tonic-gate kmem_slab_walk_init(mdb_walk_state_t *wsp)
1930Sstevel@tonic-gate {
1940Sstevel@tonic-gate 	uintptr_t caddr = wsp->walk_addr;
1950Sstevel@tonic-gate 	kmem_cache_t c;
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	if (caddr == NULL) {
1980Sstevel@tonic-gate 		mdb_warn("kmem_slab doesn't support global walks\n");
1990Sstevel@tonic-gate 		return (WALK_ERR);
2000Sstevel@tonic-gate 	}
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), caddr) == -1) {
2030Sstevel@tonic-gate 		mdb_warn("couldn't read kmem_cache at %p", caddr);
2040Sstevel@tonic-gate 		return (WALK_ERR);
2050Sstevel@tonic-gate 	}
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	wsp->walk_data =
2080Sstevel@tonic-gate 	    (void *)(caddr + offsetof(kmem_cache_t, cache_nullslab));
2090Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_next;
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	return (WALK_NEXT);
2120Sstevel@tonic-gate }
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate int
2150Sstevel@tonic-gate kmem_slab_walk_partial_init(mdb_walk_state_t *wsp)
2160Sstevel@tonic-gate {
2170Sstevel@tonic-gate 	uintptr_t caddr = wsp->walk_addr;
2180Sstevel@tonic-gate 	kmem_cache_t c;
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	if (caddr == NULL) {
2210Sstevel@tonic-gate 		mdb_warn("kmem_slab_partial doesn't support global walks\n");
2220Sstevel@tonic-gate 		return (WALK_ERR);
2230Sstevel@tonic-gate 	}
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), caddr) == -1) {
2260Sstevel@tonic-gate 		mdb_warn("couldn't read kmem_cache at %p", caddr);
2270Sstevel@tonic-gate 		return (WALK_ERR);
2280Sstevel@tonic-gate 	}
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 	wsp->walk_data =
2310Sstevel@tonic-gate 	    (void *)(caddr + offsetof(kmem_cache_t, cache_nullslab));
2320Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)c.cache_freelist;
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	/*
2350Sstevel@tonic-gate 	 * Some consumers (umem_walk_step(), in particular) require at
2360Sstevel@tonic-gate 	 * least one callback if there are any buffers in the cache.  So
2370Sstevel@tonic-gate 	 * if there are *no* partial slabs, report the last full slab, if
2380Sstevel@tonic-gate 	 * any.
2390Sstevel@tonic-gate 	 *
2400Sstevel@tonic-gate 	 * Yes, this is ugly, but it's cleaner than the other possibilities.
2410Sstevel@tonic-gate 	 */
2420Sstevel@tonic-gate 	if ((uintptr_t)wsp->walk_data == wsp->walk_addr)
2430Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_prev;
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	return (WALK_NEXT);
2460Sstevel@tonic-gate }
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate int
2490Sstevel@tonic-gate kmem_slab_walk_step(mdb_walk_state_t *wsp)
2500Sstevel@tonic-gate {
2510Sstevel@tonic-gate 	kmem_slab_t s;
2520Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
2530Sstevel@tonic-gate 	uintptr_t saddr = (uintptr_t)wsp->walk_data;
2540Sstevel@tonic-gate 	uintptr_t caddr = saddr - offsetof(kmem_cache_t, cache_nullslab);
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	if (addr == saddr)
2570Sstevel@tonic-gate 		return (WALK_DONE);
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	if (mdb_vread(&s, sizeof (s), addr) == -1) {
2600Sstevel@tonic-gate 		mdb_warn("failed to read slab at %p", wsp->walk_addr);
2610Sstevel@tonic-gate 		return (WALK_ERR);
2620Sstevel@tonic-gate 	}
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	if ((uintptr_t)s.slab_cache != caddr) {
2650Sstevel@tonic-gate 		mdb_warn("slab %p isn't in cache %p (in cache %p)\n",
2660Sstevel@tonic-gate 		    addr, caddr, s.slab_cache);
2670Sstevel@tonic-gate 		return (WALK_ERR);
2680Sstevel@tonic-gate 	}
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)s.slab_next;
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &s, wsp->walk_cbdata));
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate int
2760Sstevel@tonic-gate kmem_cache(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
2770Sstevel@tonic-gate {
2780Sstevel@tonic-gate 	kmem_cache_t c;
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
2810Sstevel@tonic-gate 		if (mdb_walk_dcmd("kmem_cache", "kmem_cache", ac, argv) == -1) {
2820Sstevel@tonic-gate 			mdb_warn("can't walk kmem_cache");
2830Sstevel@tonic-gate 			return (DCMD_ERR);
2840Sstevel@tonic-gate 		}
2850Sstevel@tonic-gate 		return (DCMD_OK);
2860Sstevel@tonic-gate 	}
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags))
2890Sstevel@tonic-gate 		mdb_printf("%-?s %-25s %4s %6s %8s %8s\n", "ADDR", "NAME",
2900Sstevel@tonic-gate 		    "FLAG", "CFLAG", "BUFSIZE", "BUFTOTL");
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), addr) == -1) {
2930Sstevel@tonic-gate 		mdb_warn("couldn't read kmem_cache at %p", addr);
2940Sstevel@tonic-gate 		return (DCMD_ERR);
2950Sstevel@tonic-gate 	}
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	mdb_printf("%0?p %-25s %04x %06x %8ld %8lld\n", addr, c.cache_name,
2980Sstevel@tonic-gate 	    c.cache_flags, c.cache_cflags, c.cache_bufsize, c.cache_buftotal);
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	return (DCMD_OK);
3010Sstevel@tonic-gate }
3020Sstevel@tonic-gate 
3034688Stomee typedef struct kmem_slab_usage {
3044688Stomee 	int ksu_refcnt;			/* count of allocated buffers on slab */
3054688Stomee } kmem_slab_usage_t;
3064688Stomee 
3074688Stomee typedef struct kmem_slab_stats {
3084688Stomee 	int ks_slabs;			/* slabs in cache */
3094688Stomee 	int ks_partial_slabs;		/* partially allocated slabs in cache */
3104688Stomee 	uint64_t ks_unused_buffers;	/* total unused buffers in cache */
3114688Stomee 	int ks_buffers_per_slab;	/* buffers per slab */
3124688Stomee 	int ks_usage_len;		/* ks_usage array length */
3134688Stomee 	kmem_slab_usage_t *ks_usage;	/* partial slab usage */
3144688Stomee 	uint_t *ks_bucket;		/* slab usage distribution */
3154688Stomee } kmem_slab_stats_t;
3164688Stomee 
3174688Stomee #define	LABEL_WIDTH	11
3184688Stomee static void
3194688Stomee kmem_slabs_print_dist(uint_t *ks_bucket, size_t buffers_per_slab,
3204688Stomee     size_t maxbuckets, size_t minbucketsize)
3214688Stomee {
3224688Stomee 	uint64_t total;
3234688Stomee 	int buckets;
3244688Stomee 	int i;
3254688Stomee 	const int *distarray;
3264688Stomee 	int complete[2];
3274688Stomee 
3284688Stomee 	buckets = buffers_per_slab;
3294688Stomee 
3304688Stomee 	total = 0;
3314688Stomee 	for (i = 0; i <= buffers_per_slab; i++)
3324688Stomee 		total += ks_bucket[i];
3334688Stomee 
3344688Stomee 	if (maxbuckets > 1)
3354688Stomee 		buckets = MIN(buckets, maxbuckets);
3364688Stomee 
3374688Stomee 	if (minbucketsize > 1) {
3384688Stomee 		/*
3394688Stomee 		 * minbucketsize does not apply to the first bucket reserved
3404688Stomee 		 * for completely allocated slabs
3414688Stomee 		 */
3424688Stomee 		buckets = MIN(buckets, 1 + ((buffers_per_slab - 1) /
3434688Stomee 		    minbucketsize));
3444688Stomee 		if ((buckets < 2) && (buffers_per_slab > 1)) {
3454688Stomee 			buckets = 2;
3464688Stomee 			minbucketsize = (buffers_per_slab - 1);
3474688Stomee 		}
3484688Stomee 	}
3494688Stomee 
3504688Stomee 	/*
3514688Stomee 	 * The first printed bucket is reserved for completely allocated slabs.
3524688Stomee 	 * Passing (buckets - 1) excludes that bucket from the generated
3534688Stomee 	 * distribution, since we're handling it as a special case.
3544688Stomee 	 */
3554688Stomee 	complete[0] = buffers_per_slab;
3564688Stomee 	complete[1] = buffers_per_slab + 1;
357*4798Stomee 	distarray = dist_linear(buckets - 1, 1, buffers_per_slab - 1);
3584688Stomee 
3594688Stomee 	mdb_printf("%*s\n", LABEL_WIDTH, "Allocated");
360*4798Stomee 	dist_print_header("Buffers", LABEL_WIDTH, "Slabs");
361*4798Stomee 
362*4798Stomee 	dist_print_bucket(complete, 0, ks_bucket, total, LABEL_WIDTH);
3634688Stomee 	/*
3644688Stomee 	 * Print bucket ranges in descending order after the first bucket for
3654688Stomee 	 * completely allocated slabs, so a person can see immediately whether
3664688Stomee 	 * or not there is fragmentation without having to scan possibly
3674688Stomee 	 * multiple screens of output. Starting at (buckets - 2) excludes the
3684688Stomee 	 * extra terminating bucket.
3694688Stomee 	 */
3704688Stomee 	for (i = buckets - 2; i >= 0; i--) {
371*4798Stomee 		dist_print_bucket(distarray, i, ks_bucket, total, LABEL_WIDTH);
3724688Stomee 	}
3734688Stomee 	mdb_printf("\n");
3744688Stomee }
3754688Stomee #undef LABEL_WIDTH
3764688Stomee 
3774688Stomee /*ARGSUSED*/
3784688Stomee static int
3794688Stomee kmem_first_slab(uintptr_t addr, const kmem_slab_t *sp, boolean_t *is_slab)
3804688Stomee {
3814688Stomee 	*is_slab = B_TRUE;
3824688Stomee 	return (WALK_DONE);
3834688Stomee }
3844688Stomee 
3854688Stomee /*ARGSUSED*/
3864688Stomee static int
3874688Stomee kmem_first_partial_slab(uintptr_t addr, const kmem_slab_t *sp,
3884688Stomee     boolean_t *is_slab)
3894688Stomee {
3904688Stomee 	/*
3914688Stomee 	 * The "kmem_partial_slab" walker reports the last full slab if there
3924688Stomee 	 * are no partial slabs (for the sake of consumers that require at least
3934688Stomee 	 * one callback if there are any buffers in the cache).
3944688Stomee 	 */
3954688Stomee 	*is_slab = ((sp->slab_refcnt > 0) &&
3964688Stomee 	    (sp->slab_refcnt < sp->slab_chunks));
3974688Stomee 	return (WALK_DONE);
3984688Stomee }
3994688Stomee 
4004688Stomee /*ARGSUSED*/
4014688Stomee static int
4024688Stomee kmem_slablist_stat(uintptr_t addr, const kmem_slab_t *sp,
4034688Stomee     kmem_slab_stats_t *ks)
4044688Stomee {
4054688Stomee 	kmem_slab_usage_t *ksu;
4064688Stomee 	long unused;
4074688Stomee 
4084688Stomee 	ks->ks_slabs++;
4094688Stomee 	if (ks->ks_buffers_per_slab == 0) {
4104688Stomee 		ks->ks_buffers_per_slab = sp->slab_chunks;
4114688Stomee 		/* +1 to include a zero bucket */
4124688Stomee 		ks->ks_bucket = mdb_zalloc((ks->ks_buffers_per_slab + 1) *
4134688Stomee 		    sizeof (*ks->ks_bucket), UM_SLEEP | UM_GC);
4144688Stomee 	}
4154688Stomee 	ks->ks_bucket[sp->slab_refcnt]++;
4164688Stomee 
4174688Stomee 	unused = (sp->slab_chunks - sp->slab_refcnt);
4184688Stomee 	if (unused == 0) {
4194688Stomee 		return (WALK_NEXT);
4204688Stomee 	}
4214688Stomee 
4224688Stomee 	ks->ks_partial_slabs++;
4234688Stomee 	ks->ks_unused_buffers += unused;
4244688Stomee 
4254688Stomee 	if (ks->ks_partial_slabs > ks->ks_usage_len) {
4264688Stomee 		kmem_slab_usage_t *usage;
4274688Stomee 		int len = ks->ks_usage_len;
4284688Stomee 
4294688Stomee 		len = (len == 0 ? 16 : len * 2);
4304688Stomee 		usage = mdb_zalloc(len * sizeof (kmem_slab_usage_t), UM_SLEEP);
4314688Stomee 		if (ks->ks_usage != NULL) {
4324688Stomee 			bcopy(ks->ks_usage, usage,
4334688Stomee 			    ks->ks_usage_len * sizeof (kmem_slab_usage_t));
4344688Stomee 			mdb_free(ks->ks_usage,
4354688Stomee 			    ks->ks_usage_len * sizeof (kmem_slab_usage_t));
4364688Stomee 		}
4374688Stomee 		ks->ks_usage = usage;
4384688Stomee 		ks->ks_usage_len = len;
4394688Stomee 	}
4404688Stomee 
4414688Stomee 	ksu = &ks->ks_usage[ks->ks_partial_slabs - 1];
4424688Stomee 	ksu->ksu_refcnt = sp->slab_refcnt;
4434688Stomee 	return (WALK_NEXT);
4444688Stomee }
4454688Stomee 
4464688Stomee static void
4474688Stomee kmem_slabs_header()
4484688Stomee {
4494688Stomee 	mdb_printf("%-25s %8s %8s %9s %9s %6s\n",
4504688Stomee 	    "", "", "Partial", "", "Unused", "");
4514688Stomee 	mdb_printf("%-25s %8s %8s %9s %9s %6s\n",
4524688Stomee 	    "Cache Name", "Slabs", "Slabs", "Buffers", "Buffers", "Waste");
4534688Stomee 	mdb_printf("%-25s %8s %8s %9s %9s %6s\n",
4544688Stomee 	    "-------------------------", "--------", "--------", "---------",
4554688Stomee 	    "---------", "------");
4564688Stomee }
4574688Stomee 
4584688Stomee int
4594688Stomee kmem_slabs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
4604688Stomee {
4614688Stomee 	kmem_cache_t c;
4624688Stomee 	kmem_slab_stats_t stats;
4634688Stomee 	mdb_walk_cb_t cb;
4644688Stomee 	int pct;
4654688Stomee 	int tenths_pct;
4664688Stomee 	size_t maxbuckets = 1;
4674688Stomee 	size_t minbucketsize = 0;
4684688Stomee 	const char *filter = NULL;
4694688Stomee 	uint_t opt_v = FALSE;
4704688Stomee 	boolean_t verbose = B_FALSE;
4714688Stomee 	boolean_t skip = B_FALSE;
4724688Stomee 
4734688Stomee 	if (mdb_getopts(argc, argv,
4744688Stomee 	    'B', MDB_OPT_UINTPTR, &minbucketsize,
4754688Stomee 	    'b', MDB_OPT_UINTPTR, &maxbuckets,
4764688Stomee 	    'n', MDB_OPT_STR, &filter,
4774688Stomee 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v,
4784688Stomee 	    NULL) != argc) {
4794688Stomee 		return (DCMD_USAGE);
4804688Stomee 	}
4814688Stomee 
4824688Stomee 	if (opt_v || (maxbuckets != 1) || (minbucketsize != 0)) {
4834688Stomee 		verbose = 1;
4844688Stomee 	}
4854688Stomee 
4864688Stomee 	if (!(flags & DCMD_ADDRSPEC)) {
4874688Stomee 		if (mdb_walk_dcmd("kmem_cache", "kmem_slabs", argc,
4884688Stomee 		    argv) == -1) {
4894688Stomee 			mdb_warn("can't walk kmem_cache");
4904688Stomee 			return (DCMD_ERR);
4914688Stomee 		}
4924688Stomee 		return (DCMD_OK);
4934688Stomee 	}
4944688Stomee 
4954688Stomee 	if (mdb_vread(&c, sizeof (c), addr) == -1) {
4964688Stomee 		mdb_warn("couldn't read kmem_cache at %p", addr);
4974688Stomee 		return (DCMD_ERR);
4984688Stomee 	}
4994688Stomee 
5004688Stomee 	if ((filter != NULL) && (strstr(c.cache_name, filter) == NULL)) {
5014688Stomee 		skip = B_TRUE;
5024688Stomee 	}
5034688Stomee 
5044688Stomee 	if (!verbose && DCMD_HDRSPEC(flags)) {
5054688Stomee 		kmem_slabs_header();
5064688Stomee 	} else if (verbose && !skip) {
5074688Stomee 		if (DCMD_HDRSPEC(flags)) {
5084688Stomee 			kmem_slabs_header();
5094688Stomee 		} else {
5104688Stomee 			boolean_t is_slab = B_FALSE;
5114688Stomee 			const char *walker_name;
5124688Stomee 			if (opt_v) {
5134688Stomee 				cb = (mdb_walk_cb_t)kmem_first_partial_slab;
5144688Stomee 				walker_name = "kmem_slab_partial";
5154688Stomee 			} else {
5164688Stomee 				cb = (mdb_walk_cb_t)kmem_first_slab;
5174688Stomee 				walker_name = "kmem_slab";
5184688Stomee 			}
5194688Stomee 			(void) mdb_pwalk(walker_name, cb, &is_slab, addr);
5204688Stomee 			if (is_slab) {
5214688Stomee 				kmem_slabs_header();
5224688Stomee 			}
5234688Stomee 		}
5244688Stomee 	}
5254688Stomee 
5264688Stomee 	if (skip) {
5274688Stomee 		return (DCMD_OK);
5284688Stomee 	}
5294688Stomee 
5304688Stomee 	bzero(&stats, sizeof (kmem_slab_stats_t));
5314688Stomee 	cb = (mdb_walk_cb_t)kmem_slablist_stat;
5324688Stomee 	(void) mdb_pwalk("kmem_slab", cb, &stats, addr);
5334688Stomee 
5344688Stomee 	if (c.cache_buftotal == 0) {
5354688Stomee 		pct = 0;
5364688Stomee 		tenths_pct = 0;
5374688Stomee 	} else {
5384688Stomee 		uint64_t n = stats.ks_unused_buffers * 10000;
5394688Stomee 		pct = (int)(n / c.cache_buftotal);
5404688Stomee 		tenths_pct = pct - ((pct / 100) * 100);
5414688Stomee 		tenths_pct = (tenths_pct + 5) / 10; /* round nearest tenth */
5424688Stomee 		if (tenths_pct == 10) {
5434688Stomee 			pct += 100;
5444688Stomee 			tenths_pct = 0;
5454688Stomee 		}
5464688Stomee 	}
5474688Stomee 
5484688Stomee 	pct /= 100;
5494688Stomee 	mdb_printf("%-25s %8d %8d %9lld %9lld %3d.%1d%%\n", c.cache_name,
5504688Stomee 	    stats.ks_slabs, stats.ks_partial_slabs, c.cache_buftotal,
5514688Stomee 	    stats.ks_unused_buffers, pct, tenths_pct);
5524688Stomee 
5534688Stomee 	if (!verbose) {
5544688Stomee 		return (DCMD_OK);
5554688Stomee 	}
5564688Stomee 
5574688Stomee 	if (maxbuckets == 0) {
5584688Stomee 		maxbuckets = stats.ks_buffers_per_slab;
5594688Stomee 	}
5604688Stomee 
5614688Stomee 	if (((maxbuckets > 1) || (minbucketsize > 0)) &&
5624688Stomee 	    (stats.ks_slabs > 0)) {
5634688Stomee 		mdb_printf("\n");
5644688Stomee 		kmem_slabs_print_dist(stats.ks_bucket,
5654688Stomee 		    stats.ks_buffers_per_slab, maxbuckets, minbucketsize);
5664688Stomee 	}
5674688Stomee 
5684688Stomee 	if (opt_v && (stats.ks_partial_slabs > 0)) {
5694688Stomee 		int i;
5704688Stomee 		kmem_slab_usage_t *ksu;
5714688Stomee 
5724688Stomee 		mdb_printf("  %d complete, %d partial",
5734688Stomee 		    (stats.ks_slabs - stats.ks_partial_slabs),
5744688Stomee 		    stats.ks_partial_slabs);
5754688Stomee 		if (stats.ks_partial_slabs > 0) {
5764688Stomee 			mdb_printf(" (%d):", stats.ks_buffers_per_slab);
5774688Stomee 		}
5784688Stomee 		for (i = 0; i < stats.ks_partial_slabs; i++) {
5794688Stomee 			ksu = &stats.ks_usage[i];
5804688Stomee 			mdb_printf(" %d", ksu->ksu_refcnt);
5814688Stomee 		}
5824688Stomee 		mdb_printf("\n\n");
5834688Stomee 	}
5844688Stomee 
5854688Stomee 	if (stats.ks_usage_len > 0) {
5864688Stomee 		mdb_free(stats.ks_usage,
5874688Stomee 		    stats.ks_usage_len * sizeof (kmem_slab_usage_t));
5884688Stomee 	}
5894688Stomee 
5904688Stomee 	return (DCMD_OK);
5914688Stomee }
5924688Stomee 
5934688Stomee void
5944688Stomee kmem_slabs_help(void)
5954688Stomee {
5964688Stomee 	mdb_printf("%s\n",
5974688Stomee "Display slab usage per kmem cache.\n");
5984688Stomee 	mdb_dec_indent(2);
5994688Stomee 	mdb_printf("%<b>OPTIONS%</b>\n");
6004688Stomee 	mdb_inc_indent(2);
6014688Stomee 	mdb_printf("%s",
6024688Stomee "  -n name\n"
6034688Stomee "        name of kmem cache (or matching partial name)\n"
6044688Stomee "  -b maxbins\n"
6054688Stomee "        Print a distribution of allocated buffers per slab using at\n"
6064688Stomee "        most maxbins bins. The first bin is reserved for completely\n"
6074688Stomee "        allocated slabs. Setting maxbins to zero (-b 0) has the same\n"
6084688Stomee "        effect as specifying the maximum allocated buffers per slab\n"
6094688Stomee "        or setting minbinsize to 1 (-B 1).\n"
6104688Stomee "  -B minbinsize\n"
6114688Stomee "        Print a distribution of allocated buffers per slab, making\n"
6124688Stomee "        all bins (except the first, reserved for completely allocated\n"
6134688Stomee "        slabs) at least minbinsize buffers apart.\n"
6144688Stomee "  -v    verbose output: List the allocated buffer count of each partial\n"
6154688Stomee "        slab on the free list in order from front to back to show how\n"
6164688Stomee "        closely the slabs are ordered by usage. For example\n"
6174688Stomee "\n"
6184688Stomee "          10 complete, 3 partial (8): 7 3 1\n"
6194688Stomee "\n"
6204688Stomee "        means there are thirteen slabs with eight buffers each, including\n"
6214688Stomee "        three partially allocated slabs with less than all eight buffers\n"
6224688Stomee "        allocated.\n"
6234688Stomee "\n"
6244688Stomee "        Buffer allocations are always from the front of the partial slab\n"
6254688Stomee "        list. When a buffer is freed from a completely used slab, that\n"
6264688Stomee "        slab is added to the front of the partial slab list. Assuming\n"
6274688Stomee "        that all buffers are equally likely to be freed soon, the\n"
6284688Stomee "        desired order of partial slabs is most-used at the front of the\n"
6294688Stomee "        list and least-used at the back (as in the example above).\n"
6304688Stomee "        However, if a slab contains an allocated buffer that will not\n"
6314688Stomee "        soon be freed, it would be better for that slab to be at the\n"
6324688Stomee "        front where it can get used up. Taking a slab off the partial\n"
6334688Stomee "        slab list (either with all buffers freed or all buffers\n"
6344688Stomee "        allocated) reduces cache fragmentation.\n"
6354688Stomee "\n"
6364688Stomee "Column\t\tDescription\n"
6374688Stomee "\n"
6384688Stomee "Cache Name\t\tname of kmem cache\n"
6394688Stomee "Slabs\t\t\ttotal slab count\n"
6404688Stomee "Partial Slabs\t\tcount of partially allocated slabs on the free list\n"
6414688Stomee "Buffers\t\ttotal buffer count (Slabs * (buffers per slab))\n"
6424688Stomee "Unused Buffers\tcount of unallocated buffers across all partial slabs\n"
6434688Stomee "Waste\t\t\t(Unused Buffers / Buffers) does not include space\n"
6444688Stomee "\t\t\t  for accounting structures (debug mode), slab\n"
6454688Stomee "\t\t\t  coloring (incremental small offsets to stagger\n"
6464688Stomee "\t\t\t  buffer alignment), or the per-CPU magazine layer\n");
6474688Stomee }
6484688Stomee 
6490Sstevel@tonic-gate static int
6500Sstevel@tonic-gate addrcmp(const void *lhs, const void *rhs)
6510Sstevel@tonic-gate {
6520Sstevel@tonic-gate 	uintptr_t p1 = *((uintptr_t *)lhs);
6530Sstevel@tonic-gate 	uintptr_t p2 = *((uintptr_t *)rhs);
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 	if (p1 < p2)
6560Sstevel@tonic-gate 		return (-1);
6570Sstevel@tonic-gate 	if (p1 > p2)
6580Sstevel@tonic-gate 		return (1);
6590Sstevel@tonic-gate 	return (0);
6600Sstevel@tonic-gate }
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate static int
6630Sstevel@tonic-gate bufctlcmp(const kmem_bufctl_audit_t **lhs, const kmem_bufctl_audit_t **rhs)
6640Sstevel@tonic-gate {
6650Sstevel@tonic-gate 	const kmem_bufctl_audit_t *bcp1 = *lhs;
6660Sstevel@tonic-gate 	const kmem_bufctl_audit_t *bcp2 = *rhs;
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	if (bcp1->bc_timestamp > bcp2->bc_timestamp)
6690Sstevel@tonic-gate 		return (-1);
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate 	if (bcp1->bc_timestamp < bcp2->bc_timestamp)
6720Sstevel@tonic-gate 		return (1);
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 	return (0);
6750Sstevel@tonic-gate }
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate typedef struct kmem_hash_walk {
6780Sstevel@tonic-gate 	uintptr_t *kmhw_table;
6790Sstevel@tonic-gate 	size_t kmhw_nelems;
6800Sstevel@tonic-gate 	size_t kmhw_pos;
6810Sstevel@tonic-gate 	kmem_bufctl_t kmhw_cur;
6820Sstevel@tonic-gate } kmem_hash_walk_t;
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate int
6850Sstevel@tonic-gate kmem_hash_walk_init(mdb_walk_state_t *wsp)
6860Sstevel@tonic-gate {
6870Sstevel@tonic-gate 	kmem_hash_walk_t *kmhw;
6880Sstevel@tonic-gate 	uintptr_t *hash;
6890Sstevel@tonic-gate 	kmem_cache_t c;
6900Sstevel@tonic-gate 	uintptr_t haddr, addr = wsp->walk_addr;
6910Sstevel@tonic-gate 	size_t nelems;
6920Sstevel@tonic-gate 	size_t hsize;
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 	if (addr == NULL) {
6950Sstevel@tonic-gate 		mdb_warn("kmem_hash doesn't support global walks\n");
6960Sstevel@tonic-gate 		return (WALK_ERR);
6970Sstevel@tonic-gate 	}
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), addr) == -1) {
7000Sstevel@tonic-gate 		mdb_warn("couldn't read cache at addr %p", addr);
7010Sstevel@tonic-gate 		return (WALK_ERR);
7020Sstevel@tonic-gate 	}
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 	if (!(c.cache_flags & KMF_HASH)) {
7050Sstevel@tonic-gate 		mdb_warn("cache %p doesn't have a hash table\n", addr);
7060Sstevel@tonic-gate 		return (WALK_DONE);		/* nothing to do */
7070Sstevel@tonic-gate 	}
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	kmhw = mdb_zalloc(sizeof (kmem_hash_walk_t), UM_SLEEP);
7100Sstevel@tonic-gate 	kmhw->kmhw_cur.bc_next = NULL;
7110Sstevel@tonic-gate 	kmhw->kmhw_pos = 0;
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	kmhw->kmhw_nelems = nelems = c.cache_hash_mask + 1;
7140Sstevel@tonic-gate 	hsize = nelems * sizeof (uintptr_t);
7150Sstevel@tonic-gate 	haddr = (uintptr_t)c.cache_hash_table;
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	kmhw->kmhw_table = hash = mdb_alloc(hsize, UM_SLEEP);
7180Sstevel@tonic-gate 	if (mdb_vread(hash, hsize, haddr) == -1) {
7190Sstevel@tonic-gate 		mdb_warn("failed to read hash table at %p", haddr);
7200Sstevel@tonic-gate 		mdb_free(hash, hsize);
7210Sstevel@tonic-gate 		mdb_free(kmhw, sizeof (kmem_hash_walk_t));
7220Sstevel@tonic-gate 		return (WALK_ERR);
7230Sstevel@tonic-gate 	}
7240Sstevel@tonic-gate 
7250Sstevel@tonic-gate 	wsp->walk_data = kmhw;
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	return (WALK_NEXT);
7280Sstevel@tonic-gate }
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate int
7310Sstevel@tonic-gate kmem_hash_walk_step(mdb_walk_state_t *wsp)
7320Sstevel@tonic-gate {
7330Sstevel@tonic-gate 	kmem_hash_walk_t *kmhw = wsp->walk_data;
7340Sstevel@tonic-gate 	uintptr_t addr = NULL;
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate 	if ((addr = (uintptr_t)kmhw->kmhw_cur.bc_next) == NULL) {
7370Sstevel@tonic-gate 		while (kmhw->kmhw_pos < kmhw->kmhw_nelems) {
7380Sstevel@tonic-gate 			if ((addr = kmhw->kmhw_table[kmhw->kmhw_pos++]) != NULL)
7390Sstevel@tonic-gate 				break;
7400Sstevel@tonic-gate 		}
7410Sstevel@tonic-gate 	}
7420Sstevel@tonic-gate 	if (addr == NULL)
7430Sstevel@tonic-gate 		return (WALK_DONE);
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate 	if (mdb_vread(&kmhw->kmhw_cur, sizeof (kmem_bufctl_t), addr) == -1) {
7460Sstevel@tonic-gate 		mdb_warn("couldn't read kmem_bufctl_t at addr %p", addr);
7470Sstevel@tonic-gate 		return (WALK_ERR);
7480Sstevel@tonic-gate 	}
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &kmhw->kmhw_cur, wsp->walk_cbdata));
7510Sstevel@tonic-gate }
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate void
7540Sstevel@tonic-gate kmem_hash_walk_fini(mdb_walk_state_t *wsp)
7550Sstevel@tonic-gate {
7560Sstevel@tonic-gate 	kmem_hash_walk_t *kmhw = wsp->walk_data;
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate 	if (kmhw == NULL)
7590Sstevel@tonic-gate 		return;
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate 	mdb_free(kmhw->kmhw_table, kmhw->kmhw_nelems * sizeof (uintptr_t));
7620Sstevel@tonic-gate 	mdb_free(kmhw, sizeof (kmem_hash_walk_t));
7630Sstevel@tonic-gate }
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate /*
7660Sstevel@tonic-gate  * Find the address of the bufctl structure for the address 'buf' in cache
7670Sstevel@tonic-gate  * 'cp', which is at address caddr, and place it in *out.
7680Sstevel@tonic-gate  */
7690Sstevel@tonic-gate static int
7700Sstevel@tonic-gate kmem_hash_lookup(kmem_cache_t *cp, uintptr_t caddr, void *buf, uintptr_t *out)
7710Sstevel@tonic-gate {
7720Sstevel@tonic-gate 	uintptr_t bucket = (uintptr_t)KMEM_HASH(cp, buf);
7730Sstevel@tonic-gate 	kmem_bufctl_t *bcp;
7740Sstevel@tonic-gate 	kmem_bufctl_t bc;
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	if (mdb_vread(&bcp, sizeof (kmem_bufctl_t *), bucket) == -1) {
7770Sstevel@tonic-gate 		mdb_warn("unable to read hash bucket for %p in cache %p",
7780Sstevel@tonic-gate 		    buf, caddr);
7790Sstevel@tonic-gate 		return (-1);
7800Sstevel@tonic-gate 	}
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate 	while (bcp != NULL) {
7830Sstevel@tonic-gate 		if (mdb_vread(&bc, sizeof (kmem_bufctl_t),
7840Sstevel@tonic-gate 		    (uintptr_t)bcp) == -1) {
7850Sstevel@tonic-gate 			mdb_warn("unable to read bufctl at %p", bcp);
7860Sstevel@tonic-gate 			return (-1);
7870Sstevel@tonic-gate 		}
7880Sstevel@tonic-gate 		if (bc.bc_addr == buf) {
7890Sstevel@tonic-gate 			*out = (uintptr_t)bcp;
7900Sstevel@tonic-gate 			return (0);
7910Sstevel@tonic-gate 		}
7920Sstevel@tonic-gate 		bcp = bc.bc_next;
7930Sstevel@tonic-gate 	}
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	mdb_warn("unable to find bufctl for %p in cache %p\n", buf, caddr);
7960Sstevel@tonic-gate 	return (-1);
7970Sstevel@tonic-gate }
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate int
8000Sstevel@tonic-gate kmem_get_magsize(const kmem_cache_t *cp)
8010Sstevel@tonic-gate {
8020Sstevel@tonic-gate 	uintptr_t addr = (uintptr_t)cp->cache_magtype;
8030Sstevel@tonic-gate 	GElf_Sym mt_sym;
8040Sstevel@tonic-gate 	kmem_magtype_t mt;
8050Sstevel@tonic-gate 	int res;
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	/*
8080Sstevel@tonic-gate 	 * if cpu 0 has a non-zero magsize, it must be correct.  caches
8090Sstevel@tonic-gate 	 * with KMF_NOMAGAZINE have disabled their magazine layers, so
8100Sstevel@tonic-gate 	 * it is okay to return 0 for them.
8110Sstevel@tonic-gate 	 */
8120Sstevel@tonic-gate 	if ((res = cp->cache_cpu[0].cc_magsize) != 0 ||
8130Sstevel@tonic-gate 	    (cp->cache_flags & KMF_NOMAGAZINE))
8140Sstevel@tonic-gate 		return (res);
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate 	if (mdb_lookup_by_name("kmem_magtype", &mt_sym) == -1) {
8170Sstevel@tonic-gate 		mdb_warn("unable to read 'kmem_magtype'");
8180Sstevel@tonic-gate 	} else if (addr < mt_sym.st_value ||
8190Sstevel@tonic-gate 	    addr + sizeof (mt) - 1 > mt_sym.st_value + mt_sym.st_size - 1 ||
8200Sstevel@tonic-gate 	    ((addr - mt_sym.st_value) % sizeof (mt)) != 0) {
8210Sstevel@tonic-gate 		mdb_warn("cache '%s' has invalid magtype pointer (%p)\n",
8220Sstevel@tonic-gate 		    cp->cache_name, addr);
8230Sstevel@tonic-gate 		return (0);
8240Sstevel@tonic-gate 	}
8250Sstevel@tonic-gate 	if (mdb_vread(&mt, sizeof (mt), addr) == -1) {
8260Sstevel@tonic-gate 		mdb_warn("unable to read magtype at %a", addr);
8270Sstevel@tonic-gate 		return (0);
8280Sstevel@tonic-gate 	}
8290Sstevel@tonic-gate 	return (mt.mt_magsize);
8300Sstevel@tonic-gate }
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate /*ARGSUSED*/
8330Sstevel@tonic-gate static int
8340Sstevel@tonic-gate kmem_estimate_slab(uintptr_t addr, const kmem_slab_t *sp, size_t *est)
8350Sstevel@tonic-gate {
8360Sstevel@tonic-gate 	*est -= (sp->slab_chunks - sp->slab_refcnt);
8370Sstevel@tonic-gate 
8380Sstevel@tonic-gate 	return (WALK_NEXT);
8390Sstevel@tonic-gate }
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate /*
8420Sstevel@tonic-gate  * Returns an upper bound on the number of allocated buffers in a given
8430Sstevel@tonic-gate  * cache.
8440Sstevel@tonic-gate  */
8450Sstevel@tonic-gate size_t
8460Sstevel@tonic-gate kmem_estimate_allocated(uintptr_t addr, const kmem_cache_t *cp)
8470Sstevel@tonic-gate {
8480Sstevel@tonic-gate 	int magsize;
8490Sstevel@tonic-gate 	size_t cache_est;
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	cache_est = cp->cache_buftotal;
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 	(void) mdb_pwalk("kmem_slab_partial",
8540Sstevel@tonic-gate 	    (mdb_walk_cb_t)kmem_estimate_slab, &cache_est, addr);
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 	if ((magsize = kmem_get_magsize(cp)) != 0) {
8570Sstevel@tonic-gate 		size_t mag_est = cp->cache_full.ml_total * magsize;
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 		if (cache_est >= mag_est) {
8600Sstevel@tonic-gate 			cache_est -= mag_est;
8610Sstevel@tonic-gate 		} else {
8620Sstevel@tonic-gate 			mdb_warn("cache %p's magazine layer holds more buffers "
8630Sstevel@tonic-gate 			    "than the slab layer.\n", addr);
8640Sstevel@tonic-gate 		}
8650Sstevel@tonic-gate 	}
8660Sstevel@tonic-gate 	return (cache_est);
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate #define	READMAG_ROUNDS(rounds) { \
8700Sstevel@tonic-gate 	if (mdb_vread(mp, magbsize, (uintptr_t)kmp) == -1) { \
8710Sstevel@tonic-gate 		mdb_warn("couldn't read magazine at %p", kmp); \
8720Sstevel@tonic-gate 		goto fail; \
8730Sstevel@tonic-gate 	} \
8740Sstevel@tonic-gate 	for (i = 0; i < rounds; i++) { \
8750Sstevel@tonic-gate 		maglist[magcnt++] = mp->mag_round[i]; \
8760Sstevel@tonic-gate 		if (magcnt == magmax) { \
8770Sstevel@tonic-gate 			mdb_warn("%d magazines exceeds fudge factor\n", \
8780Sstevel@tonic-gate 			    magcnt); \
8790Sstevel@tonic-gate 			goto fail; \
8800Sstevel@tonic-gate 		} \
8810Sstevel@tonic-gate 	} \
8820Sstevel@tonic-gate }
8830Sstevel@tonic-gate 
8840Sstevel@tonic-gate int
8850Sstevel@tonic-gate kmem_read_magazines(kmem_cache_t *cp, uintptr_t addr, int ncpus,
8860Sstevel@tonic-gate     void ***maglistp, size_t *magcntp, size_t *magmaxp, int alloc_flags)
8870Sstevel@tonic-gate {
8880Sstevel@tonic-gate 	kmem_magazine_t *kmp, *mp;
8890Sstevel@tonic-gate 	void **maglist = NULL;
8900Sstevel@tonic-gate 	int i, cpu;
8910Sstevel@tonic-gate 	size_t magsize, magmax, magbsize;
8920Sstevel@tonic-gate 	size_t magcnt = 0;
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	/*
8950Sstevel@tonic-gate 	 * Read the magtype out of the cache, after verifying the pointer's
8960Sstevel@tonic-gate 	 * correctness.
8970Sstevel@tonic-gate 	 */
8980Sstevel@tonic-gate 	magsize = kmem_get_magsize(cp);
8991528Sjwadams 	if (magsize == 0) {
9001528Sjwadams 		*maglistp = NULL;
9011528Sjwadams 		*magcntp = 0;
9021528Sjwadams 		*magmaxp = 0;
9031528Sjwadams 		return (WALK_NEXT);
9041528Sjwadams 	}
9050Sstevel@tonic-gate 
9060Sstevel@tonic-gate 	/*
9070Sstevel@tonic-gate 	 * There are several places where we need to go buffer hunting:
9080Sstevel@tonic-gate 	 * the per-CPU loaded magazine, the per-CPU spare full magazine,
9090Sstevel@tonic-gate 	 * and the full magazine list in the depot.
9100Sstevel@tonic-gate 	 *
9110Sstevel@tonic-gate 	 * For an upper bound on the number of buffers in the magazine
9120Sstevel@tonic-gate 	 * layer, we have the number of magazines on the cache_full
9130Sstevel@tonic-gate 	 * list plus at most two magazines per CPU (the loaded and the
9140Sstevel@tonic-gate 	 * spare).  Toss in 100 magazines as a fudge factor in case this
9150Sstevel@tonic-gate 	 * is live (the number "100" comes from the same fudge factor in
9160Sstevel@tonic-gate 	 * crash(1M)).
9170Sstevel@tonic-gate 	 */
9180Sstevel@tonic-gate 	magmax = (cp->cache_full.ml_total + 2 * ncpus + 100) * magsize;
9190Sstevel@tonic-gate 	magbsize = offsetof(kmem_magazine_t, mag_round[magsize]);
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	if (magbsize >= PAGESIZE / 2) {
9220Sstevel@tonic-gate 		mdb_warn("magazine size for cache %p unreasonable (%x)\n",
9230Sstevel@tonic-gate 		    addr, magbsize);
9241528Sjwadams 		return (WALK_ERR);
9250Sstevel@tonic-gate 	}
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 	maglist = mdb_alloc(magmax * sizeof (void *), alloc_flags);
9280Sstevel@tonic-gate 	mp = mdb_alloc(magbsize, alloc_flags);
9290Sstevel@tonic-gate 	if (mp == NULL || maglist == NULL)
9300Sstevel@tonic-gate 		goto fail;
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate 	/*
9330Sstevel@tonic-gate 	 * First up: the magazines in the depot (i.e. on the cache_full list).
9340Sstevel@tonic-gate 	 */
9350Sstevel@tonic-gate 	for (kmp = cp->cache_full.ml_list; kmp != NULL; ) {
9360Sstevel@tonic-gate 		READMAG_ROUNDS(magsize);
9370Sstevel@tonic-gate 		kmp = mp->mag_next;
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate 		if (kmp == cp->cache_full.ml_list)
9400Sstevel@tonic-gate 			break; /* cache_full list loop detected */
9410Sstevel@tonic-gate 	}
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate 	dprintf(("cache_full list done\n"));
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 	/*
9460Sstevel@tonic-gate 	 * Now whip through the CPUs, snagging the loaded magazines
9470Sstevel@tonic-gate 	 * and full spares.
9480Sstevel@tonic-gate 	 */
9490Sstevel@tonic-gate 	for (cpu = 0; cpu < ncpus; cpu++) {
9500Sstevel@tonic-gate 		kmem_cpu_cache_t *ccp = &cp->cache_cpu[cpu];
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate 		dprintf(("reading cpu cache %p\n",
9530Sstevel@tonic-gate 		    (uintptr_t)ccp - (uintptr_t)cp + addr));
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate 		if (ccp->cc_rounds > 0 &&
9560Sstevel@tonic-gate 		    (kmp = ccp->cc_loaded) != NULL) {
9570Sstevel@tonic-gate 			dprintf(("reading %d loaded rounds\n", ccp->cc_rounds));
9580Sstevel@tonic-gate 			READMAG_ROUNDS(ccp->cc_rounds);
9590Sstevel@tonic-gate 		}
9600Sstevel@tonic-gate 
9610Sstevel@tonic-gate 		if (ccp->cc_prounds > 0 &&
9620Sstevel@tonic-gate 		    (kmp = ccp->cc_ploaded) != NULL) {
9630Sstevel@tonic-gate 			dprintf(("reading %d previously loaded rounds\n",
9640Sstevel@tonic-gate 			    ccp->cc_prounds));
9650Sstevel@tonic-gate 			READMAG_ROUNDS(ccp->cc_prounds);
9660Sstevel@tonic-gate 		}
9670Sstevel@tonic-gate 	}
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 	dprintf(("magazine layer: %d buffers\n", magcnt));
9700Sstevel@tonic-gate 
9710Sstevel@tonic-gate 	if (!(alloc_flags & UM_GC))
9720Sstevel@tonic-gate 		mdb_free(mp, magbsize);
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate 	*maglistp = maglist;
9750Sstevel@tonic-gate 	*magcntp = magcnt;
9760Sstevel@tonic-gate 	*magmaxp = magmax;
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	return (WALK_NEXT);
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate fail:
9810Sstevel@tonic-gate 	if (!(alloc_flags & UM_GC)) {
9820Sstevel@tonic-gate 		if (mp)
9830Sstevel@tonic-gate 			mdb_free(mp, magbsize);
9840Sstevel@tonic-gate 		if (maglist)
9850Sstevel@tonic-gate 			mdb_free(maglist, magmax * sizeof (void *));
9860Sstevel@tonic-gate 	}
9870Sstevel@tonic-gate 	return (WALK_ERR);
9880Sstevel@tonic-gate }
9890Sstevel@tonic-gate 
9900Sstevel@tonic-gate static int
9910Sstevel@tonic-gate kmem_walk_callback(mdb_walk_state_t *wsp, uintptr_t buf)
9920Sstevel@tonic-gate {
9930Sstevel@tonic-gate 	return (wsp->walk_callback(buf, NULL, wsp->walk_cbdata));
9940Sstevel@tonic-gate }
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate static int
9970Sstevel@tonic-gate bufctl_walk_callback(kmem_cache_t *cp, mdb_walk_state_t *wsp, uintptr_t buf)
9980Sstevel@tonic-gate {
9990Sstevel@tonic-gate 	kmem_bufctl_audit_t b;
10000Sstevel@tonic-gate 
10010Sstevel@tonic-gate 	/*
10020Sstevel@tonic-gate 	 * if KMF_AUDIT is not set, we know that we're looking at a
10030Sstevel@tonic-gate 	 * kmem_bufctl_t.
10040Sstevel@tonic-gate 	 */
10050Sstevel@tonic-gate 	if (!(cp->cache_flags & KMF_AUDIT) ||
10060Sstevel@tonic-gate 	    mdb_vread(&b, sizeof (kmem_bufctl_audit_t), buf) == -1) {
10070Sstevel@tonic-gate 		(void) memset(&b, 0, sizeof (b));
10080Sstevel@tonic-gate 		if (mdb_vread(&b, sizeof (kmem_bufctl_t), buf) == -1) {
10090Sstevel@tonic-gate 			mdb_warn("unable to read bufctl at %p", buf);
10100Sstevel@tonic-gate 			return (WALK_ERR);
10110Sstevel@tonic-gate 		}
10120Sstevel@tonic-gate 	}
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 	return (wsp->walk_callback(buf, &b, wsp->walk_cbdata));
10150Sstevel@tonic-gate }
10160Sstevel@tonic-gate 
10170Sstevel@tonic-gate typedef struct kmem_walk {
10180Sstevel@tonic-gate 	int kmw_type;
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate 	int kmw_addr;			/* cache address */
10210Sstevel@tonic-gate 	kmem_cache_t *kmw_cp;
10220Sstevel@tonic-gate 	size_t kmw_csize;
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 	/*
10250Sstevel@tonic-gate 	 * magazine layer
10260Sstevel@tonic-gate 	 */
10270Sstevel@tonic-gate 	void **kmw_maglist;
10280Sstevel@tonic-gate 	size_t kmw_max;
10290Sstevel@tonic-gate 	size_t kmw_count;
10300Sstevel@tonic-gate 	size_t kmw_pos;
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate 	/*
10330Sstevel@tonic-gate 	 * slab layer
10340Sstevel@tonic-gate 	 */
10350Sstevel@tonic-gate 	char *kmw_valid;	/* to keep track of freed buffers */
10360Sstevel@tonic-gate 	char *kmw_ubase;	/* buffer for slab data */
10370Sstevel@tonic-gate } kmem_walk_t;
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate static int
10400Sstevel@tonic-gate kmem_walk_init_common(mdb_walk_state_t *wsp, int type)
10410Sstevel@tonic-gate {
10420Sstevel@tonic-gate 	kmem_walk_t *kmw;
10430Sstevel@tonic-gate 	int ncpus, csize;
10440Sstevel@tonic-gate 	kmem_cache_t *cp;
10451528Sjwadams 	size_t vm_quantum;
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate 	size_t magmax, magcnt;
10480Sstevel@tonic-gate 	void **maglist = NULL;
10490Sstevel@tonic-gate 	uint_t chunksize, slabsize;
10500Sstevel@tonic-gate 	int status = WALK_ERR;
10510Sstevel@tonic-gate 	uintptr_t addr = wsp->walk_addr;
10520Sstevel@tonic-gate 	const char *layered;
10530Sstevel@tonic-gate 
10540Sstevel@tonic-gate 	type &= ~KM_HASH;
10550Sstevel@tonic-gate 
10560Sstevel@tonic-gate 	if (addr == NULL) {
10570Sstevel@tonic-gate 		mdb_warn("kmem walk doesn't support global walks\n");
10580Sstevel@tonic-gate 		return (WALK_ERR);
10590Sstevel@tonic-gate 	}
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 	dprintf(("walking %p\n", addr));
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate 	/*
10640Sstevel@tonic-gate 	 * First we need to figure out how many CPUs are configured in the
10650Sstevel@tonic-gate 	 * system to know how much to slurp out.
10660Sstevel@tonic-gate 	 */
10670Sstevel@tonic-gate 	mdb_readvar(&ncpus, "max_ncpus");
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	csize = KMEM_CACHE_SIZE(ncpus);
10700Sstevel@tonic-gate 	cp = mdb_alloc(csize, UM_SLEEP);
10710Sstevel@tonic-gate 
10720Sstevel@tonic-gate 	if (mdb_vread(cp, csize, addr) == -1) {
10730Sstevel@tonic-gate 		mdb_warn("couldn't read cache at addr %p", addr);
10740Sstevel@tonic-gate 		goto out2;
10750Sstevel@tonic-gate 	}
10760Sstevel@tonic-gate 
10771528Sjwadams 	/*
10781528Sjwadams 	 * It's easy for someone to hand us an invalid cache address.
10791528Sjwadams 	 * Unfortunately, it is hard for this walker to survive an
10801528Sjwadams 	 * invalid cache cleanly.  So we make sure that:
10811528Sjwadams 	 *
10821528Sjwadams 	 *	1. the vmem arena for the cache is readable,
10831528Sjwadams 	 *	2. the vmem arena's quantum is a power of 2,
10841528Sjwadams 	 *	3. our slabsize is a multiple of the quantum, and
10851528Sjwadams 	 *	4. our chunksize is >0 and less than our slabsize.
10861528Sjwadams 	 */
10871528Sjwadams 	if (mdb_vread(&vm_quantum, sizeof (vm_quantum),
10881528Sjwadams 	    (uintptr_t)&cp->cache_arena->vm_quantum) == -1 ||
10891528Sjwadams 	    vm_quantum == 0 ||
10901528Sjwadams 	    (vm_quantum & (vm_quantum - 1)) != 0 ||
10911528Sjwadams 	    cp->cache_slabsize < vm_quantum ||
10921528Sjwadams 	    P2PHASE(cp->cache_slabsize, vm_quantum) != 0 ||
10931528Sjwadams 	    cp->cache_chunksize == 0 ||
10941528Sjwadams 	    cp->cache_chunksize > cp->cache_slabsize) {
10951528Sjwadams 		mdb_warn("%p is not a valid kmem_cache_t\n", addr);
10961528Sjwadams 		goto out2;
10971528Sjwadams 	}
10981528Sjwadams 
10990Sstevel@tonic-gate 	dprintf(("buf total is %d\n", cp->cache_buftotal));
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate 	if (cp->cache_buftotal == 0) {
11020Sstevel@tonic-gate 		mdb_free(cp, csize);
11030Sstevel@tonic-gate 		return (WALK_DONE);
11040Sstevel@tonic-gate 	}
11050Sstevel@tonic-gate 
11060Sstevel@tonic-gate 	/*
11070Sstevel@tonic-gate 	 * If they ask for bufctls, but it's a small-slab cache,
11080Sstevel@tonic-gate 	 * there is nothing to report.
11090Sstevel@tonic-gate 	 */
11100Sstevel@tonic-gate 	if ((type & KM_BUFCTL) && !(cp->cache_flags & KMF_HASH)) {
11110Sstevel@tonic-gate 		dprintf(("bufctl requested, not KMF_HASH (flags: %p)\n",
11120Sstevel@tonic-gate 		    cp->cache_flags));
11130Sstevel@tonic-gate 		mdb_free(cp, csize);
11140Sstevel@tonic-gate 		return (WALK_DONE);
11150Sstevel@tonic-gate 	}
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 	/*
11180Sstevel@tonic-gate 	 * If they want constructed buffers, but there's no constructor or
11190Sstevel@tonic-gate 	 * the cache has DEADBEEF checking enabled, there is nothing to report.
11200Sstevel@tonic-gate 	 */
11210Sstevel@tonic-gate 	if ((type & KM_CONSTRUCTED) && (!(type & KM_FREE) ||
11220Sstevel@tonic-gate 	    cp->cache_constructor == NULL ||
11230Sstevel@tonic-gate 	    (cp->cache_flags & (KMF_DEADBEEF | KMF_LITE)) == KMF_DEADBEEF)) {
11240Sstevel@tonic-gate 		mdb_free(cp, csize);
11250Sstevel@tonic-gate 		return (WALK_DONE);
11260Sstevel@tonic-gate 	}
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 	/*
11290Sstevel@tonic-gate 	 * Read in the contents of the magazine layer
11300Sstevel@tonic-gate 	 */
11310Sstevel@tonic-gate 	if (kmem_read_magazines(cp, addr, ncpus, &maglist, &magcnt,
11320Sstevel@tonic-gate 	    &magmax, UM_SLEEP) == WALK_ERR)
11330Sstevel@tonic-gate 		goto out2;
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 	/*
11360Sstevel@tonic-gate 	 * We have all of the buffers from the magazines;  if we are walking
11370Sstevel@tonic-gate 	 * allocated buffers, sort them so we can bsearch them later.
11380Sstevel@tonic-gate 	 */
11390Sstevel@tonic-gate 	if (type & KM_ALLOCATED)
11400Sstevel@tonic-gate 		qsort(maglist, magcnt, sizeof (void *), addrcmp);
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	wsp->walk_data = kmw = mdb_zalloc(sizeof (kmem_walk_t), UM_SLEEP);
11430Sstevel@tonic-gate 
11440Sstevel@tonic-gate 	kmw->kmw_type = type;
11450Sstevel@tonic-gate 	kmw->kmw_addr = addr;
11460Sstevel@tonic-gate 	kmw->kmw_cp = cp;
11470Sstevel@tonic-gate 	kmw->kmw_csize = csize;
11480Sstevel@tonic-gate 	kmw->kmw_maglist = maglist;
11490Sstevel@tonic-gate 	kmw->kmw_max = magmax;
11500Sstevel@tonic-gate 	kmw->kmw_count = magcnt;
11510Sstevel@tonic-gate 	kmw->kmw_pos = 0;
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	/*
11540Sstevel@tonic-gate 	 * When walking allocated buffers in a KMF_HASH cache, we walk the
11550Sstevel@tonic-gate 	 * hash table instead of the slab layer.
11560Sstevel@tonic-gate 	 */
11570Sstevel@tonic-gate 	if ((cp->cache_flags & KMF_HASH) && (type & KM_ALLOCATED)) {
11580Sstevel@tonic-gate 		layered = "kmem_hash";
11590Sstevel@tonic-gate 
11600Sstevel@tonic-gate 		kmw->kmw_type |= KM_HASH;
11610Sstevel@tonic-gate 	} else {
11620Sstevel@tonic-gate 		/*
11630Sstevel@tonic-gate 		 * If we are walking freed buffers, we only need the
11640Sstevel@tonic-gate 		 * magazine layer plus the partially allocated slabs.
11650Sstevel@tonic-gate 		 * To walk allocated buffers, we need all of the slabs.
11660Sstevel@tonic-gate 		 */
11670Sstevel@tonic-gate 		if (type & KM_ALLOCATED)
11680Sstevel@tonic-gate 			layered = "kmem_slab";
11690Sstevel@tonic-gate 		else
11700Sstevel@tonic-gate 			layered = "kmem_slab_partial";
11710Sstevel@tonic-gate 
11720Sstevel@tonic-gate 		/*
11730Sstevel@tonic-gate 		 * for small-slab caches, we read in the entire slab.  For
11740Sstevel@tonic-gate 		 * freed buffers, we can just walk the freelist.  For
11750Sstevel@tonic-gate 		 * allocated buffers, we use a 'valid' array to track
11760Sstevel@tonic-gate 		 * the freed buffers.
11770Sstevel@tonic-gate 		 */
11780Sstevel@tonic-gate 		if (!(cp->cache_flags & KMF_HASH)) {
11790Sstevel@tonic-gate 			chunksize = cp->cache_chunksize;
11800Sstevel@tonic-gate 			slabsize = cp->cache_slabsize;
11810Sstevel@tonic-gate 
11820Sstevel@tonic-gate 			kmw->kmw_ubase = mdb_alloc(slabsize +
11830Sstevel@tonic-gate 			    sizeof (kmem_bufctl_t), UM_SLEEP);
11840Sstevel@tonic-gate 
11850Sstevel@tonic-gate 			if (type & KM_ALLOCATED)
11860Sstevel@tonic-gate 				kmw->kmw_valid =
11870Sstevel@tonic-gate 				    mdb_alloc(slabsize / chunksize, UM_SLEEP);
11880Sstevel@tonic-gate 		}
11890Sstevel@tonic-gate 	}
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate 	status = WALK_NEXT;
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate 	if (mdb_layered_walk(layered, wsp) == -1) {
11940Sstevel@tonic-gate 		mdb_warn("unable to start layered '%s' walk", layered);
11950Sstevel@tonic-gate 		status = WALK_ERR;
11960Sstevel@tonic-gate 	}
11970Sstevel@tonic-gate 
11980Sstevel@tonic-gate out1:
11990Sstevel@tonic-gate 	if (status == WALK_ERR) {
12000Sstevel@tonic-gate 		if (kmw->kmw_valid)
12010Sstevel@tonic-gate 			mdb_free(kmw->kmw_valid, slabsize / chunksize);
12020Sstevel@tonic-gate 
12030Sstevel@tonic-gate 		if (kmw->kmw_ubase)
12040Sstevel@tonic-gate 			mdb_free(kmw->kmw_ubase, slabsize +
12050Sstevel@tonic-gate 			    sizeof (kmem_bufctl_t));
12060Sstevel@tonic-gate 
12071528Sjwadams 		if (kmw->kmw_maglist)
12081528Sjwadams 			mdb_free(kmw->kmw_maglist,
12091528Sjwadams 			    kmw->kmw_max * sizeof (uintptr_t));
12101528Sjwadams 
12110Sstevel@tonic-gate 		mdb_free(kmw, sizeof (kmem_walk_t));
12120Sstevel@tonic-gate 		wsp->walk_data = NULL;
12130Sstevel@tonic-gate 	}
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate out2:
12160Sstevel@tonic-gate 	if (status == WALK_ERR)
12170Sstevel@tonic-gate 		mdb_free(cp, csize);
12180Sstevel@tonic-gate 
12190Sstevel@tonic-gate 	return (status);
12200Sstevel@tonic-gate }
12210Sstevel@tonic-gate 
12220Sstevel@tonic-gate int
12230Sstevel@tonic-gate kmem_walk_step(mdb_walk_state_t *wsp)
12240Sstevel@tonic-gate {
12250Sstevel@tonic-gate 	kmem_walk_t *kmw = wsp->walk_data;
12260Sstevel@tonic-gate 	int type = kmw->kmw_type;
12270Sstevel@tonic-gate 	kmem_cache_t *cp = kmw->kmw_cp;
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate 	void **maglist = kmw->kmw_maglist;
12300Sstevel@tonic-gate 	int magcnt = kmw->kmw_count;
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 	uintptr_t chunksize, slabsize;
12330Sstevel@tonic-gate 	uintptr_t addr;
12340Sstevel@tonic-gate 	const kmem_slab_t *sp;
12350Sstevel@tonic-gate 	const kmem_bufctl_t *bcp;
12360Sstevel@tonic-gate 	kmem_bufctl_t bc;
12370Sstevel@tonic-gate 
12380Sstevel@tonic-gate 	int chunks;
12390Sstevel@tonic-gate 	char *kbase;
12400Sstevel@tonic-gate 	void *buf;
12410Sstevel@tonic-gate 	int i, ret;
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 	char *valid, *ubase;
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate 	/*
12460Sstevel@tonic-gate 	 * first, handle the 'kmem_hash' layered walk case
12470Sstevel@tonic-gate 	 */
12480Sstevel@tonic-gate 	if (type & KM_HASH) {
12490Sstevel@tonic-gate 		/*
12500Sstevel@tonic-gate 		 * We have a buffer which has been allocated out of the
12510Sstevel@tonic-gate 		 * global layer. We need to make sure that it's not
12520Sstevel@tonic-gate 		 * actually sitting in a magazine before we report it as
12530Sstevel@tonic-gate 		 * an allocated buffer.
12540Sstevel@tonic-gate 		 */
12550Sstevel@tonic-gate 		buf = ((const kmem_bufctl_t *)wsp->walk_layer)->bc_addr;
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 		if (magcnt > 0 &&
12580Sstevel@tonic-gate 		    bsearch(&buf, maglist, magcnt, sizeof (void *),
12590Sstevel@tonic-gate 		    addrcmp) != NULL)
12600Sstevel@tonic-gate 			return (WALK_NEXT);
12610Sstevel@tonic-gate 
12620Sstevel@tonic-gate 		if (type & KM_BUFCTL)
12630Sstevel@tonic-gate 			return (bufctl_walk_callback(cp, wsp, wsp->walk_addr));
12640Sstevel@tonic-gate 
12650Sstevel@tonic-gate 		return (kmem_walk_callback(wsp, (uintptr_t)buf));
12660Sstevel@tonic-gate 	}
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 	ret = WALK_NEXT;
12690Sstevel@tonic-gate 
12700Sstevel@tonic-gate 	addr = kmw->kmw_addr;
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 	/*
12730Sstevel@tonic-gate 	 * If we're walking freed buffers, report everything in the
12740Sstevel@tonic-gate 	 * magazine layer before processing the first slab.
12750Sstevel@tonic-gate 	 */
12760Sstevel@tonic-gate 	if ((type & KM_FREE) && magcnt != 0) {
12770Sstevel@tonic-gate 		kmw->kmw_count = 0;		/* only do this once */
12780Sstevel@tonic-gate 		for (i = 0; i < magcnt; i++) {
12790Sstevel@tonic-gate 			buf = maglist[i];
12800Sstevel@tonic-gate 
12810Sstevel@tonic-gate 			if (type & KM_BUFCTL) {
12820Sstevel@tonic-gate 				uintptr_t out;
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate 				if (cp->cache_flags & KMF_BUFTAG) {
12850Sstevel@tonic-gate 					kmem_buftag_t *btp;
12860Sstevel@tonic-gate 					kmem_buftag_t tag;
12870Sstevel@tonic-gate 
12880Sstevel@tonic-gate 					/* LINTED - alignment */
12890Sstevel@tonic-gate 					btp = KMEM_BUFTAG(cp, buf);
12900Sstevel@tonic-gate 					if (mdb_vread(&tag, sizeof (tag),
12910Sstevel@tonic-gate 					    (uintptr_t)btp) == -1) {
12920Sstevel@tonic-gate 						mdb_warn("reading buftag for "
12930Sstevel@tonic-gate 						    "%p at %p", buf, btp);
12940Sstevel@tonic-gate 						continue;
12950Sstevel@tonic-gate 					}
12960Sstevel@tonic-gate 					out = (uintptr_t)tag.bt_bufctl;
12970Sstevel@tonic-gate 				} else {
12980Sstevel@tonic-gate 					if (kmem_hash_lookup(cp, addr, buf,
12990Sstevel@tonic-gate 					    &out) == -1)
13000Sstevel@tonic-gate 						continue;
13010Sstevel@tonic-gate 				}
13020Sstevel@tonic-gate 				ret = bufctl_walk_callback(cp, wsp, out);
13030Sstevel@tonic-gate 			} else {
13040Sstevel@tonic-gate 				ret = kmem_walk_callback(wsp, (uintptr_t)buf);
13050Sstevel@tonic-gate 			}
13060Sstevel@tonic-gate 
13070Sstevel@tonic-gate 			if (ret != WALK_NEXT)
13080Sstevel@tonic-gate 				return (ret);
13090Sstevel@tonic-gate 		}
13100Sstevel@tonic-gate 	}
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 	/*
13130Sstevel@tonic-gate 	 * If they want constructed buffers, we're finished, since the
13140Sstevel@tonic-gate 	 * magazine layer holds them all.
13150Sstevel@tonic-gate 	 */
13160Sstevel@tonic-gate 	if (type & KM_CONSTRUCTED)
13170Sstevel@tonic-gate 		return (WALK_DONE);
13180Sstevel@tonic-gate 
13190Sstevel@tonic-gate 	/*
13200Sstevel@tonic-gate 	 * Handle the buffers in the current slab
13210Sstevel@tonic-gate 	 */
13220Sstevel@tonic-gate 	chunksize = cp->cache_chunksize;
13230Sstevel@tonic-gate 	slabsize = cp->cache_slabsize;
13240Sstevel@tonic-gate 
13250Sstevel@tonic-gate 	sp = wsp->walk_layer;
13260Sstevel@tonic-gate 	chunks = sp->slab_chunks;
13270Sstevel@tonic-gate 	kbase = sp->slab_base;
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 	dprintf(("kbase is %p\n", kbase));
13300Sstevel@tonic-gate 
13310Sstevel@tonic-gate 	if (!(cp->cache_flags & KMF_HASH)) {
13320Sstevel@tonic-gate 		valid = kmw->kmw_valid;
13330Sstevel@tonic-gate 		ubase = kmw->kmw_ubase;
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate 		if (mdb_vread(ubase, chunks * chunksize,
13360Sstevel@tonic-gate 		    (uintptr_t)kbase) == -1) {
13370Sstevel@tonic-gate 			mdb_warn("failed to read slab contents at %p", kbase);
13380Sstevel@tonic-gate 			return (WALK_ERR);
13390Sstevel@tonic-gate 		}
13400Sstevel@tonic-gate 
13410Sstevel@tonic-gate 		/*
13420Sstevel@tonic-gate 		 * Set up the valid map as fully allocated -- we'll punch
13430Sstevel@tonic-gate 		 * out the freelist.
13440Sstevel@tonic-gate 		 */
13450Sstevel@tonic-gate 		if (type & KM_ALLOCATED)
13460Sstevel@tonic-gate 			(void) memset(valid, 1, chunks);
13470Sstevel@tonic-gate 	} else {
13480Sstevel@tonic-gate 		valid = NULL;
13490Sstevel@tonic-gate 		ubase = NULL;
13500Sstevel@tonic-gate 	}
13510Sstevel@tonic-gate 
13520Sstevel@tonic-gate 	/*
13530Sstevel@tonic-gate 	 * walk the slab's freelist
13540Sstevel@tonic-gate 	 */
13550Sstevel@tonic-gate 	bcp = sp->slab_head;
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate 	dprintf(("refcnt is %d; chunks is %d\n", sp->slab_refcnt, chunks));
13580Sstevel@tonic-gate 
13590Sstevel@tonic-gate 	/*
13600Sstevel@tonic-gate 	 * since we could be in the middle of allocating a buffer,
13610Sstevel@tonic-gate 	 * our refcnt could be one higher than it aught.  So we
13620Sstevel@tonic-gate 	 * check one further on the freelist than the count allows.
13630Sstevel@tonic-gate 	 */
13640Sstevel@tonic-gate 	for (i = sp->slab_refcnt; i <= chunks; i++) {
13650Sstevel@tonic-gate 		uint_t ndx;
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate 		dprintf(("bcp is %p\n", bcp));
13680Sstevel@tonic-gate 
13690Sstevel@tonic-gate 		if (bcp == NULL) {
13700Sstevel@tonic-gate 			if (i == chunks)
13710Sstevel@tonic-gate 				break;
13720Sstevel@tonic-gate 			mdb_warn(
13730Sstevel@tonic-gate 			    "slab %p in cache %p freelist too short by %d\n",
13740Sstevel@tonic-gate 			    sp, addr, chunks - i);
13750Sstevel@tonic-gate 			break;
13760Sstevel@tonic-gate 		}
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 		if (cp->cache_flags & KMF_HASH) {
13790Sstevel@tonic-gate 			if (mdb_vread(&bc, sizeof (bc), (uintptr_t)bcp) == -1) {
13800Sstevel@tonic-gate 				mdb_warn("failed to read bufctl ptr at %p",
13810Sstevel@tonic-gate 				    bcp);
13820Sstevel@tonic-gate 				break;
13830Sstevel@tonic-gate 			}
13840Sstevel@tonic-gate 			buf = bc.bc_addr;
13850Sstevel@tonic-gate 		} else {
13860Sstevel@tonic-gate 			/*
13870Sstevel@tonic-gate 			 * Otherwise the buffer is in the slab which
13880Sstevel@tonic-gate 			 * we've read in;  we just need to determine
13890Sstevel@tonic-gate 			 * its offset in the slab to find the
13900Sstevel@tonic-gate 			 * kmem_bufctl_t.
13910Sstevel@tonic-gate 			 */
13920Sstevel@tonic-gate 			bc = *((kmem_bufctl_t *)
13930Sstevel@tonic-gate 			    ((uintptr_t)bcp - (uintptr_t)kbase +
13940Sstevel@tonic-gate 			    (uintptr_t)ubase));
13950Sstevel@tonic-gate 
13960Sstevel@tonic-gate 			buf = KMEM_BUF(cp, bcp);
13970Sstevel@tonic-gate 		}
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 		ndx = ((uintptr_t)buf - (uintptr_t)kbase) / chunksize;
14000Sstevel@tonic-gate 
14010Sstevel@tonic-gate 		if (ndx > slabsize / cp->cache_bufsize) {
14020Sstevel@tonic-gate 			/*
14030Sstevel@tonic-gate 			 * This is very wrong; we have managed to find
14040Sstevel@tonic-gate 			 * a buffer in the slab which shouldn't
14050Sstevel@tonic-gate 			 * actually be here.  Emit a warning, and
14060Sstevel@tonic-gate 			 * try to continue.
14070Sstevel@tonic-gate 			 */
14080Sstevel@tonic-gate 			mdb_warn("buf %p is out of range for "
14090Sstevel@tonic-gate 			    "slab %p, cache %p\n", buf, sp, addr);
14100Sstevel@tonic-gate 		} else if (type & KM_ALLOCATED) {
14110Sstevel@tonic-gate 			/*
14120Sstevel@tonic-gate 			 * we have found a buffer on the slab's freelist;
14130Sstevel@tonic-gate 			 * clear its entry
14140Sstevel@tonic-gate 			 */
14150Sstevel@tonic-gate 			valid[ndx] = 0;
14160Sstevel@tonic-gate 		} else {
14170Sstevel@tonic-gate 			/*
14180Sstevel@tonic-gate 			 * Report this freed buffer
14190Sstevel@tonic-gate 			 */
14200Sstevel@tonic-gate 			if (type & KM_BUFCTL) {
14210Sstevel@tonic-gate 				ret = bufctl_walk_callback(cp, wsp,
14220Sstevel@tonic-gate 				    (uintptr_t)bcp);
14230Sstevel@tonic-gate 			} else {
14240Sstevel@tonic-gate 				ret = kmem_walk_callback(wsp, (uintptr_t)buf);
14250Sstevel@tonic-gate 			}
14260Sstevel@tonic-gate 			if (ret != WALK_NEXT)
14270Sstevel@tonic-gate 				return (ret);
14280Sstevel@tonic-gate 		}
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate 		bcp = bc.bc_next;
14310Sstevel@tonic-gate 	}
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 	if (bcp != NULL) {
14340Sstevel@tonic-gate 		dprintf(("slab %p in cache %p freelist too long (%p)\n",
14350Sstevel@tonic-gate 		    sp, addr, bcp));
14360Sstevel@tonic-gate 	}
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate 	/*
14390Sstevel@tonic-gate 	 * If we are walking freed buffers, the loop above handled reporting
14400Sstevel@tonic-gate 	 * them.
14410Sstevel@tonic-gate 	 */
14420Sstevel@tonic-gate 	if (type & KM_FREE)
14430Sstevel@tonic-gate 		return (WALK_NEXT);
14440Sstevel@tonic-gate 
14450Sstevel@tonic-gate 	if (type & KM_BUFCTL) {
14460Sstevel@tonic-gate 		mdb_warn("impossible situation: small-slab KM_BUFCTL walk for "
14470Sstevel@tonic-gate 		    "cache %p\n", addr);
14480Sstevel@tonic-gate 		return (WALK_ERR);
14490Sstevel@tonic-gate 	}
14500Sstevel@tonic-gate 
14510Sstevel@tonic-gate 	/*
14520Sstevel@tonic-gate 	 * Report allocated buffers, skipping buffers in the magazine layer.
14530Sstevel@tonic-gate 	 * We only get this far for small-slab caches.
14540Sstevel@tonic-gate 	 */
14550Sstevel@tonic-gate 	for (i = 0; ret == WALK_NEXT && i < chunks; i++) {
14560Sstevel@tonic-gate 		buf = (char *)kbase + i * chunksize;
14570Sstevel@tonic-gate 
14580Sstevel@tonic-gate 		if (!valid[i])
14590Sstevel@tonic-gate 			continue;		/* on slab freelist */
14600Sstevel@tonic-gate 
14610Sstevel@tonic-gate 		if (magcnt > 0 &&
14620Sstevel@tonic-gate 		    bsearch(&buf, maglist, magcnt, sizeof (void *),
14630Sstevel@tonic-gate 		    addrcmp) != NULL)
14640Sstevel@tonic-gate 			continue;		/* in magazine layer */
14650Sstevel@tonic-gate 
14660Sstevel@tonic-gate 		ret = kmem_walk_callback(wsp, (uintptr_t)buf);
14670Sstevel@tonic-gate 	}
14680Sstevel@tonic-gate 	return (ret);
14690Sstevel@tonic-gate }
14700Sstevel@tonic-gate 
14710Sstevel@tonic-gate void
14720Sstevel@tonic-gate kmem_walk_fini(mdb_walk_state_t *wsp)
14730Sstevel@tonic-gate {
14740Sstevel@tonic-gate 	kmem_walk_t *kmw = wsp->walk_data;
14750Sstevel@tonic-gate 	uintptr_t chunksize;
14760Sstevel@tonic-gate 	uintptr_t slabsize;
14770Sstevel@tonic-gate 
14780Sstevel@tonic-gate 	if (kmw == NULL)
14790Sstevel@tonic-gate 		return;
14800Sstevel@tonic-gate 
14810Sstevel@tonic-gate 	if (kmw->kmw_maglist != NULL)
14820Sstevel@tonic-gate 		mdb_free(kmw->kmw_maglist, kmw->kmw_max * sizeof (void *));
14830Sstevel@tonic-gate 
14840Sstevel@tonic-gate 	chunksize = kmw->kmw_cp->cache_chunksize;
14850Sstevel@tonic-gate 	slabsize = kmw->kmw_cp->cache_slabsize;
14860Sstevel@tonic-gate 
14870Sstevel@tonic-gate 	if (kmw->kmw_valid != NULL)
14880Sstevel@tonic-gate 		mdb_free(kmw->kmw_valid, slabsize / chunksize);
14890Sstevel@tonic-gate 	if (kmw->kmw_ubase != NULL)
14900Sstevel@tonic-gate 		mdb_free(kmw->kmw_ubase, slabsize + sizeof (kmem_bufctl_t));
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 	mdb_free(kmw->kmw_cp, kmw->kmw_csize);
14930Sstevel@tonic-gate 	mdb_free(kmw, sizeof (kmem_walk_t));
14940Sstevel@tonic-gate }
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate /*ARGSUSED*/
14970Sstevel@tonic-gate static int
14980Sstevel@tonic-gate kmem_walk_all(uintptr_t addr, const kmem_cache_t *c, mdb_walk_state_t *wsp)
14990Sstevel@tonic-gate {
15000Sstevel@tonic-gate 	/*
15010Sstevel@tonic-gate 	 * Buffers allocated from NOTOUCH caches can also show up as freed
15020Sstevel@tonic-gate 	 * memory in other caches.  This can be a little confusing, so we
15030Sstevel@tonic-gate 	 * don't walk NOTOUCH caches when walking all caches (thereby assuring
15040Sstevel@tonic-gate 	 * that "::walk kmem" and "::walk freemem" yield disjoint output).
15050Sstevel@tonic-gate 	 */
15060Sstevel@tonic-gate 	if (c->cache_cflags & KMC_NOTOUCH)
15070Sstevel@tonic-gate 		return (WALK_NEXT);
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate 	if (mdb_pwalk(wsp->walk_data, wsp->walk_callback,
15100Sstevel@tonic-gate 	    wsp->walk_cbdata, addr) == -1)
15110Sstevel@tonic-gate 		return (WALK_DONE);
15120Sstevel@tonic-gate 
15130Sstevel@tonic-gate 	return (WALK_NEXT);
15140Sstevel@tonic-gate }
15150Sstevel@tonic-gate 
15160Sstevel@tonic-gate #define	KMEM_WALK_ALL(name, wsp) { \
15170Sstevel@tonic-gate 	wsp->walk_data = (name); \
15180Sstevel@tonic-gate 	if (mdb_walk("kmem_cache", (mdb_walk_cb_t)kmem_walk_all, wsp) == -1) \
15190Sstevel@tonic-gate 		return (WALK_ERR); \
15200Sstevel@tonic-gate 	return (WALK_DONE); \
15210Sstevel@tonic-gate }
15220Sstevel@tonic-gate 
15230Sstevel@tonic-gate int
15240Sstevel@tonic-gate kmem_walk_init(mdb_walk_state_t *wsp)
15250Sstevel@tonic-gate {
15260Sstevel@tonic-gate 	if (wsp->walk_arg != NULL)
15270Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)wsp->walk_arg;
15280Sstevel@tonic-gate 
15290Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
15300Sstevel@tonic-gate 		KMEM_WALK_ALL("kmem", wsp);
15310Sstevel@tonic-gate 	return (kmem_walk_init_common(wsp, KM_ALLOCATED));
15320Sstevel@tonic-gate }
15330Sstevel@tonic-gate 
15340Sstevel@tonic-gate int
15350Sstevel@tonic-gate bufctl_walk_init(mdb_walk_state_t *wsp)
15360Sstevel@tonic-gate {
15370Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
15380Sstevel@tonic-gate 		KMEM_WALK_ALL("bufctl", wsp);
15390Sstevel@tonic-gate 	return (kmem_walk_init_common(wsp, KM_ALLOCATED | KM_BUFCTL));
15400Sstevel@tonic-gate }
15410Sstevel@tonic-gate 
15420Sstevel@tonic-gate int
15430Sstevel@tonic-gate freemem_walk_init(mdb_walk_state_t *wsp)
15440Sstevel@tonic-gate {
15450Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
15460Sstevel@tonic-gate 		KMEM_WALK_ALL("freemem", wsp);
15470Sstevel@tonic-gate 	return (kmem_walk_init_common(wsp, KM_FREE));
15480Sstevel@tonic-gate }
15490Sstevel@tonic-gate 
15500Sstevel@tonic-gate int
15510Sstevel@tonic-gate freemem_constructed_walk_init(mdb_walk_state_t *wsp)
15520Sstevel@tonic-gate {
15530Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
15540Sstevel@tonic-gate 		KMEM_WALK_ALL("freemem_constructed", wsp);
15550Sstevel@tonic-gate 	return (kmem_walk_init_common(wsp, KM_FREE | KM_CONSTRUCTED));
15560Sstevel@tonic-gate }
15570Sstevel@tonic-gate 
15580Sstevel@tonic-gate int
15590Sstevel@tonic-gate freectl_walk_init(mdb_walk_state_t *wsp)
15600Sstevel@tonic-gate {
15610Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
15620Sstevel@tonic-gate 		KMEM_WALK_ALL("freectl", wsp);
15630Sstevel@tonic-gate 	return (kmem_walk_init_common(wsp, KM_FREE | KM_BUFCTL));
15640Sstevel@tonic-gate }
15650Sstevel@tonic-gate 
15660Sstevel@tonic-gate int
15670Sstevel@tonic-gate freectl_constructed_walk_init(mdb_walk_state_t *wsp)
15680Sstevel@tonic-gate {
15690Sstevel@tonic-gate 	if (wsp->walk_addr == NULL)
15700Sstevel@tonic-gate 		KMEM_WALK_ALL("freectl_constructed", wsp);
15710Sstevel@tonic-gate 	return (kmem_walk_init_common(wsp,
15720Sstevel@tonic-gate 	    KM_FREE | KM_BUFCTL | KM_CONSTRUCTED));
15730Sstevel@tonic-gate }
15740Sstevel@tonic-gate 
15750Sstevel@tonic-gate typedef struct bufctl_history_walk {
15760Sstevel@tonic-gate 	void		*bhw_next;
15770Sstevel@tonic-gate 	kmem_cache_t	*bhw_cache;
15780Sstevel@tonic-gate 	kmem_slab_t	*bhw_slab;
15790Sstevel@tonic-gate 	hrtime_t	bhw_timestamp;
15800Sstevel@tonic-gate } bufctl_history_walk_t;
15810Sstevel@tonic-gate 
15820Sstevel@tonic-gate int
15830Sstevel@tonic-gate bufctl_history_walk_init(mdb_walk_state_t *wsp)
15840Sstevel@tonic-gate {
15850Sstevel@tonic-gate 	bufctl_history_walk_t *bhw;
15860Sstevel@tonic-gate 	kmem_bufctl_audit_t bc;
15870Sstevel@tonic-gate 	kmem_bufctl_audit_t bcn;
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
15900Sstevel@tonic-gate 		mdb_warn("bufctl_history walk doesn't support global walks\n");
15910Sstevel@tonic-gate 		return (WALK_ERR);
15920Sstevel@tonic-gate 	}
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate 	if (mdb_vread(&bc, sizeof (bc), wsp->walk_addr) == -1) {
15950Sstevel@tonic-gate 		mdb_warn("unable to read bufctl at %p", wsp->walk_addr);
15960Sstevel@tonic-gate 		return (WALK_ERR);
15970Sstevel@tonic-gate 	}
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate 	bhw = mdb_zalloc(sizeof (*bhw), UM_SLEEP);
16000Sstevel@tonic-gate 	bhw->bhw_timestamp = 0;
16010Sstevel@tonic-gate 	bhw->bhw_cache = bc.bc_cache;
16020Sstevel@tonic-gate 	bhw->bhw_slab = bc.bc_slab;
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate 	/*
16050Sstevel@tonic-gate 	 * sometimes the first log entry matches the base bufctl;  in that
16060Sstevel@tonic-gate 	 * case, skip the base bufctl.
16070Sstevel@tonic-gate 	 */
16080Sstevel@tonic-gate 	if (bc.bc_lastlog != NULL &&
16090Sstevel@tonic-gate 	    mdb_vread(&bcn, sizeof (bcn), (uintptr_t)bc.bc_lastlog) != -1 &&
16100Sstevel@tonic-gate 	    bc.bc_addr == bcn.bc_addr &&
16110Sstevel@tonic-gate 	    bc.bc_cache == bcn.bc_cache &&
16120Sstevel@tonic-gate 	    bc.bc_slab == bcn.bc_slab &&
16130Sstevel@tonic-gate 	    bc.bc_timestamp == bcn.bc_timestamp &&
16140Sstevel@tonic-gate 	    bc.bc_thread == bcn.bc_thread)
16150Sstevel@tonic-gate 		bhw->bhw_next = bc.bc_lastlog;
16160Sstevel@tonic-gate 	else
16170Sstevel@tonic-gate 		bhw->bhw_next = (void *)wsp->walk_addr;
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)bc.bc_addr;
16200Sstevel@tonic-gate 	wsp->walk_data = bhw;
16210Sstevel@tonic-gate 
16220Sstevel@tonic-gate 	return (WALK_NEXT);
16230Sstevel@tonic-gate }
16240Sstevel@tonic-gate 
16250Sstevel@tonic-gate int
16260Sstevel@tonic-gate bufctl_history_walk_step(mdb_walk_state_t *wsp)
16270Sstevel@tonic-gate {
16280Sstevel@tonic-gate 	bufctl_history_walk_t *bhw = wsp->walk_data;
16290Sstevel@tonic-gate 	uintptr_t addr = (uintptr_t)bhw->bhw_next;
16300Sstevel@tonic-gate 	uintptr_t baseaddr = wsp->walk_addr;
16310Sstevel@tonic-gate 	kmem_bufctl_audit_t bc;
16320Sstevel@tonic-gate 
16330Sstevel@tonic-gate 	if (addr == NULL)
16340Sstevel@tonic-gate 		return (WALK_DONE);
16350Sstevel@tonic-gate 
16360Sstevel@tonic-gate 	if (mdb_vread(&bc, sizeof (bc), addr) == -1) {
16370Sstevel@tonic-gate 		mdb_warn("unable to read bufctl at %p", bhw->bhw_next);
16380Sstevel@tonic-gate 		return (WALK_ERR);
16390Sstevel@tonic-gate 	}
16400Sstevel@tonic-gate 
16410Sstevel@tonic-gate 	/*
16420Sstevel@tonic-gate 	 * The bufctl is only valid if the address, cache, and slab are
16430Sstevel@tonic-gate 	 * correct.  We also check that the timestamp is decreasing, to
16440Sstevel@tonic-gate 	 * prevent infinite loops.
16450Sstevel@tonic-gate 	 */
16460Sstevel@tonic-gate 	if ((uintptr_t)bc.bc_addr != baseaddr ||
16470Sstevel@tonic-gate 	    bc.bc_cache != bhw->bhw_cache ||
16480Sstevel@tonic-gate 	    bc.bc_slab != bhw->bhw_slab ||
16490Sstevel@tonic-gate 	    (bhw->bhw_timestamp != 0 && bc.bc_timestamp >= bhw->bhw_timestamp))
16500Sstevel@tonic-gate 		return (WALK_DONE);
16510Sstevel@tonic-gate 
16520Sstevel@tonic-gate 	bhw->bhw_next = bc.bc_lastlog;
16530Sstevel@tonic-gate 	bhw->bhw_timestamp = bc.bc_timestamp;
16540Sstevel@tonic-gate 
16550Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &bc, wsp->walk_cbdata));
16560Sstevel@tonic-gate }
16570Sstevel@tonic-gate 
16580Sstevel@tonic-gate void
16590Sstevel@tonic-gate bufctl_history_walk_fini(mdb_walk_state_t *wsp)
16600Sstevel@tonic-gate {
16610Sstevel@tonic-gate 	bufctl_history_walk_t *bhw = wsp->walk_data;
16620Sstevel@tonic-gate 
16630Sstevel@tonic-gate 	mdb_free(bhw, sizeof (*bhw));
16640Sstevel@tonic-gate }
16650Sstevel@tonic-gate 
16660Sstevel@tonic-gate typedef struct kmem_log_walk {
16670Sstevel@tonic-gate 	kmem_bufctl_audit_t *klw_base;
16680Sstevel@tonic-gate 	kmem_bufctl_audit_t **klw_sorted;
16690Sstevel@tonic-gate 	kmem_log_header_t klw_lh;
16700Sstevel@tonic-gate 	size_t klw_size;
16710Sstevel@tonic-gate 	size_t klw_maxndx;
16720Sstevel@tonic-gate 	size_t klw_ndx;
16730Sstevel@tonic-gate } kmem_log_walk_t;
16740Sstevel@tonic-gate 
16750Sstevel@tonic-gate int
16760Sstevel@tonic-gate kmem_log_walk_init(mdb_walk_state_t *wsp)
16770Sstevel@tonic-gate {
16780Sstevel@tonic-gate 	uintptr_t lp = wsp->walk_addr;
16790Sstevel@tonic-gate 	kmem_log_walk_t *klw;
16800Sstevel@tonic-gate 	kmem_log_header_t *lhp;
16810Sstevel@tonic-gate 	int maxndx, i, j, k;
16820Sstevel@tonic-gate 
16830Sstevel@tonic-gate 	/*
16840Sstevel@tonic-gate 	 * By default (global walk), walk the kmem_transaction_log.  Otherwise
16850Sstevel@tonic-gate 	 * read the log whose kmem_log_header_t is stored at walk_addr.
16860Sstevel@tonic-gate 	 */
16870Sstevel@tonic-gate 	if (lp == NULL && mdb_readvar(&lp, "kmem_transaction_log") == -1) {
16880Sstevel@tonic-gate 		mdb_warn("failed to read 'kmem_transaction_log'");
16890Sstevel@tonic-gate 		return (WALK_ERR);
16900Sstevel@tonic-gate 	}
16910Sstevel@tonic-gate 
16920Sstevel@tonic-gate 	if (lp == NULL) {
16930Sstevel@tonic-gate 		mdb_warn("log is disabled\n");
16940Sstevel@tonic-gate 		return (WALK_ERR);
16950Sstevel@tonic-gate 	}
16960Sstevel@tonic-gate 
16970Sstevel@tonic-gate 	klw = mdb_zalloc(sizeof (kmem_log_walk_t), UM_SLEEP);
16980Sstevel@tonic-gate 	lhp = &klw->klw_lh;
16990Sstevel@tonic-gate 
17000Sstevel@tonic-gate 	if (mdb_vread(lhp, sizeof (kmem_log_header_t), lp) == -1) {
17010Sstevel@tonic-gate 		mdb_warn("failed to read log header at %p", lp);
17020Sstevel@tonic-gate 		mdb_free(klw, sizeof (kmem_log_walk_t));
17030Sstevel@tonic-gate 		return (WALK_ERR);
17040Sstevel@tonic-gate 	}
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate 	klw->klw_size = lhp->lh_chunksize * lhp->lh_nchunks;
17070Sstevel@tonic-gate 	klw->klw_base = mdb_alloc(klw->klw_size, UM_SLEEP);
17080Sstevel@tonic-gate 	maxndx = lhp->lh_chunksize / sizeof (kmem_bufctl_audit_t) - 1;
17090Sstevel@tonic-gate 
17100Sstevel@tonic-gate 	if (mdb_vread(klw->klw_base, klw->klw_size,
17110Sstevel@tonic-gate 	    (uintptr_t)lhp->lh_base) == -1) {
17120Sstevel@tonic-gate 		mdb_warn("failed to read log at base %p", lhp->lh_base);
17130Sstevel@tonic-gate 		mdb_free(klw->klw_base, klw->klw_size);
17140Sstevel@tonic-gate 		mdb_free(klw, sizeof (kmem_log_walk_t));
17150Sstevel@tonic-gate 		return (WALK_ERR);
17160Sstevel@tonic-gate 	}
17170Sstevel@tonic-gate 
17180Sstevel@tonic-gate 	klw->klw_sorted = mdb_alloc(maxndx * lhp->lh_nchunks *
17190Sstevel@tonic-gate 	    sizeof (kmem_bufctl_audit_t *), UM_SLEEP);
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate 	for (i = 0, k = 0; i < lhp->lh_nchunks; i++) {
17220Sstevel@tonic-gate 		kmem_bufctl_audit_t *chunk = (kmem_bufctl_audit_t *)
17230Sstevel@tonic-gate 		    ((uintptr_t)klw->klw_base + i * lhp->lh_chunksize);
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate 		for (j = 0; j < maxndx; j++)
17260Sstevel@tonic-gate 			klw->klw_sorted[k++] = &chunk[j];
17270Sstevel@tonic-gate 	}
17280Sstevel@tonic-gate 
17290Sstevel@tonic-gate 	qsort(klw->klw_sorted, k, sizeof (kmem_bufctl_audit_t *),
17300Sstevel@tonic-gate 	    (int(*)(const void *, const void *))bufctlcmp);
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate 	klw->klw_maxndx = k;
17330Sstevel@tonic-gate 	wsp->walk_data = klw;
17340Sstevel@tonic-gate 
17350Sstevel@tonic-gate 	return (WALK_NEXT);
17360Sstevel@tonic-gate }
17370Sstevel@tonic-gate 
17380Sstevel@tonic-gate int
17390Sstevel@tonic-gate kmem_log_walk_step(mdb_walk_state_t *wsp)
17400Sstevel@tonic-gate {
17410Sstevel@tonic-gate 	kmem_log_walk_t *klw = wsp->walk_data;
17420Sstevel@tonic-gate 	kmem_bufctl_audit_t *bcp;
17430Sstevel@tonic-gate 
17440Sstevel@tonic-gate 	if (klw->klw_ndx == klw->klw_maxndx)
17450Sstevel@tonic-gate 		return (WALK_DONE);
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 	bcp = klw->klw_sorted[klw->klw_ndx++];
17480Sstevel@tonic-gate 
17490Sstevel@tonic-gate 	return (wsp->walk_callback((uintptr_t)bcp - (uintptr_t)klw->klw_base +
17500Sstevel@tonic-gate 	    (uintptr_t)klw->klw_lh.lh_base, bcp, wsp->walk_cbdata));
17510Sstevel@tonic-gate }
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate void
17540Sstevel@tonic-gate kmem_log_walk_fini(mdb_walk_state_t *wsp)
17550Sstevel@tonic-gate {
17560Sstevel@tonic-gate 	kmem_log_walk_t *klw = wsp->walk_data;
17570Sstevel@tonic-gate 
17580Sstevel@tonic-gate 	mdb_free(klw->klw_base, klw->klw_size);
17590Sstevel@tonic-gate 	mdb_free(klw->klw_sorted, klw->klw_maxndx *
17600Sstevel@tonic-gate 	    sizeof (kmem_bufctl_audit_t *));
17610Sstevel@tonic-gate 	mdb_free(klw, sizeof (kmem_log_walk_t));
17620Sstevel@tonic-gate }
17630Sstevel@tonic-gate 
17640Sstevel@tonic-gate typedef struct allocdby_bufctl {
17650Sstevel@tonic-gate 	uintptr_t abb_addr;
17660Sstevel@tonic-gate 	hrtime_t abb_ts;
17670Sstevel@tonic-gate } allocdby_bufctl_t;
17680Sstevel@tonic-gate 
17690Sstevel@tonic-gate typedef struct allocdby_walk {
17700Sstevel@tonic-gate 	const char *abw_walk;
17710Sstevel@tonic-gate 	uintptr_t abw_thread;
17720Sstevel@tonic-gate 	size_t abw_nbufs;
17730Sstevel@tonic-gate 	size_t abw_size;
17740Sstevel@tonic-gate 	allocdby_bufctl_t *abw_buf;
17750Sstevel@tonic-gate 	size_t abw_ndx;
17760Sstevel@tonic-gate } allocdby_walk_t;
17770Sstevel@tonic-gate 
17780Sstevel@tonic-gate int
17790Sstevel@tonic-gate allocdby_walk_bufctl(uintptr_t addr, const kmem_bufctl_audit_t *bcp,
17800Sstevel@tonic-gate     allocdby_walk_t *abw)
17810Sstevel@tonic-gate {
17820Sstevel@tonic-gate 	if ((uintptr_t)bcp->bc_thread != abw->abw_thread)
17830Sstevel@tonic-gate 		return (WALK_NEXT);
17840Sstevel@tonic-gate 
17850Sstevel@tonic-gate 	if (abw->abw_nbufs == abw->abw_size) {
17860Sstevel@tonic-gate 		allocdby_bufctl_t *buf;
17870Sstevel@tonic-gate 		size_t oldsize = sizeof (allocdby_bufctl_t) * abw->abw_size;
17880Sstevel@tonic-gate 
17890Sstevel@tonic-gate 		buf = mdb_zalloc(oldsize << 1, UM_SLEEP);
17900Sstevel@tonic-gate 
17910Sstevel@tonic-gate 		bcopy(abw->abw_buf, buf, oldsize);
17920Sstevel@tonic-gate 		mdb_free(abw->abw_buf, oldsize);
17930Sstevel@tonic-gate 
17940Sstevel@tonic-gate 		abw->abw_size <<= 1;
17950Sstevel@tonic-gate 		abw->abw_buf = buf;
17960Sstevel@tonic-gate 	}
17970Sstevel@tonic-gate 
17980Sstevel@tonic-gate 	abw->abw_buf[abw->abw_nbufs].abb_addr = addr;
17990Sstevel@tonic-gate 	abw->abw_buf[abw->abw_nbufs].abb_ts = bcp->bc_timestamp;
18000Sstevel@tonic-gate 	abw->abw_nbufs++;
18010Sstevel@tonic-gate 
18020Sstevel@tonic-gate 	return (WALK_NEXT);
18030Sstevel@tonic-gate }
18040Sstevel@tonic-gate 
18050Sstevel@tonic-gate /*ARGSUSED*/
18060Sstevel@tonic-gate int
18070Sstevel@tonic-gate allocdby_walk_cache(uintptr_t addr, const kmem_cache_t *c, allocdby_walk_t *abw)
18080Sstevel@tonic-gate {
18090Sstevel@tonic-gate 	if (mdb_pwalk(abw->abw_walk, (mdb_walk_cb_t)allocdby_walk_bufctl,
18100Sstevel@tonic-gate 	    abw, addr) == -1) {
18110Sstevel@tonic-gate 		mdb_warn("couldn't walk bufctl for cache %p", addr);
18120Sstevel@tonic-gate 		return (WALK_DONE);
18130Sstevel@tonic-gate 	}
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate 	return (WALK_NEXT);
18160Sstevel@tonic-gate }
18170Sstevel@tonic-gate 
18180Sstevel@tonic-gate static int
18190Sstevel@tonic-gate allocdby_cmp(const allocdby_bufctl_t *lhs, const allocdby_bufctl_t *rhs)
18200Sstevel@tonic-gate {
18210Sstevel@tonic-gate 	if (lhs->abb_ts < rhs->abb_ts)
18220Sstevel@tonic-gate 		return (1);
18230Sstevel@tonic-gate 	if (lhs->abb_ts > rhs->abb_ts)
18240Sstevel@tonic-gate 		return (-1);
18250Sstevel@tonic-gate 	return (0);
18260Sstevel@tonic-gate }
18270Sstevel@tonic-gate 
18280Sstevel@tonic-gate static int
18290Sstevel@tonic-gate allocdby_walk_init_common(mdb_walk_state_t *wsp, const char *walk)
18300Sstevel@tonic-gate {
18310Sstevel@tonic-gate 	allocdby_walk_t *abw;
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
18340Sstevel@tonic-gate 		mdb_warn("allocdby walk doesn't support global walks\n");
18350Sstevel@tonic-gate 		return (WALK_ERR);
18360Sstevel@tonic-gate 	}
18370Sstevel@tonic-gate 
18380Sstevel@tonic-gate 	abw = mdb_zalloc(sizeof (allocdby_walk_t), UM_SLEEP);
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate 	abw->abw_thread = wsp->walk_addr;
18410Sstevel@tonic-gate 	abw->abw_walk = walk;
18420Sstevel@tonic-gate 	abw->abw_size = 128;	/* something reasonable */
18430Sstevel@tonic-gate 	abw->abw_buf =
18440Sstevel@tonic-gate 	    mdb_zalloc(abw->abw_size * sizeof (allocdby_bufctl_t), UM_SLEEP);
18450Sstevel@tonic-gate 
18460Sstevel@tonic-gate 	wsp->walk_data = abw;
18470Sstevel@tonic-gate 
18480Sstevel@tonic-gate 	if (mdb_walk("kmem_cache",
18490Sstevel@tonic-gate 	    (mdb_walk_cb_t)allocdby_walk_cache, abw) == -1) {
18500Sstevel@tonic-gate 		mdb_warn("couldn't walk kmem_cache");
18510Sstevel@tonic-gate 		allocdby_walk_fini(wsp);
18520Sstevel@tonic-gate 		return (WALK_ERR);
18530Sstevel@tonic-gate 	}
18540Sstevel@tonic-gate 
18550Sstevel@tonic-gate 	qsort(abw->abw_buf, abw->abw_nbufs, sizeof (allocdby_bufctl_t),
18560Sstevel@tonic-gate 	    (int(*)(const void *, const void *))allocdby_cmp);
18570Sstevel@tonic-gate 
18580Sstevel@tonic-gate 	return (WALK_NEXT);
18590Sstevel@tonic-gate }
18600Sstevel@tonic-gate 
18610Sstevel@tonic-gate int
18620Sstevel@tonic-gate allocdby_walk_init(mdb_walk_state_t *wsp)
18630Sstevel@tonic-gate {
18640Sstevel@tonic-gate 	return (allocdby_walk_init_common(wsp, "bufctl"));
18650Sstevel@tonic-gate }
18660Sstevel@tonic-gate 
18670Sstevel@tonic-gate int
18680Sstevel@tonic-gate freedby_walk_init(mdb_walk_state_t *wsp)
18690Sstevel@tonic-gate {
18700Sstevel@tonic-gate 	return (allocdby_walk_init_common(wsp, "freectl"));
18710Sstevel@tonic-gate }
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate int
18740Sstevel@tonic-gate allocdby_walk_step(mdb_walk_state_t *wsp)
18750Sstevel@tonic-gate {
18760Sstevel@tonic-gate 	allocdby_walk_t *abw = wsp->walk_data;
18770Sstevel@tonic-gate 	kmem_bufctl_audit_t bc;
18780Sstevel@tonic-gate 	uintptr_t addr;
18790Sstevel@tonic-gate 
18800Sstevel@tonic-gate 	if (abw->abw_ndx == abw->abw_nbufs)
18810Sstevel@tonic-gate 		return (WALK_DONE);
18820Sstevel@tonic-gate 
18830Sstevel@tonic-gate 	addr = abw->abw_buf[abw->abw_ndx++].abb_addr;
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate 	if (mdb_vread(&bc, sizeof (bc), addr) == -1) {
18860Sstevel@tonic-gate 		mdb_warn("couldn't read bufctl at %p", addr);
18870Sstevel@tonic-gate 		return (WALK_DONE);
18880Sstevel@tonic-gate 	}
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &bc, wsp->walk_cbdata));
18910Sstevel@tonic-gate }
18920Sstevel@tonic-gate 
18930Sstevel@tonic-gate void
18940Sstevel@tonic-gate allocdby_walk_fini(mdb_walk_state_t *wsp)
18950Sstevel@tonic-gate {
18960Sstevel@tonic-gate 	allocdby_walk_t *abw = wsp->walk_data;
18970Sstevel@tonic-gate 
18980Sstevel@tonic-gate 	mdb_free(abw->abw_buf, sizeof (allocdby_bufctl_t) * abw->abw_size);
18990Sstevel@tonic-gate 	mdb_free(abw, sizeof (allocdby_walk_t));
19000Sstevel@tonic-gate }
19010Sstevel@tonic-gate 
19020Sstevel@tonic-gate /*ARGSUSED*/
19030Sstevel@tonic-gate int
19040Sstevel@tonic-gate allocdby_walk(uintptr_t addr, const kmem_bufctl_audit_t *bcp, void *ignored)
19050Sstevel@tonic-gate {
19060Sstevel@tonic-gate 	char c[MDB_SYM_NAMLEN];
19070Sstevel@tonic-gate 	GElf_Sym sym;
19080Sstevel@tonic-gate 	int i;
19090Sstevel@tonic-gate 
19100Sstevel@tonic-gate 	mdb_printf("%0?p %12llx ", addr, bcp->bc_timestamp);
19110Sstevel@tonic-gate 	for (i = 0; i < bcp->bc_depth; i++) {
19120Sstevel@tonic-gate 		if (mdb_lookup_by_addr(bcp->bc_stack[i],
19130Sstevel@tonic-gate 		    MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1)
19140Sstevel@tonic-gate 			continue;
19150Sstevel@tonic-gate 		if (strncmp(c, "kmem_", 5) == 0)
19160Sstevel@tonic-gate 			continue;
19170Sstevel@tonic-gate 		mdb_printf("%s+0x%lx",
19180Sstevel@tonic-gate 		    c, bcp->bc_stack[i] - (uintptr_t)sym.st_value);
19190Sstevel@tonic-gate 		break;
19200Sstevel@tonic-gate 	}
19210Sstevel@tonic-gate 	mdb_printf("\n");
19220Sstevel@tonic-gate 
19230Sstevel@tonic-gate 	return (WALK_NEXT);
19240Sstevel@tonic-gate }
19250Sstevel@tonic-gate 
19260Sstevel@tonic-gate static int
19270Sstevel@tonic-gate allocdby_common(uintptr_t addr, uint_t flags, const char *w)
19280Sstevel@tonic-gate {
19290Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
19300Sstevel@tonic-gate 		return (DCMD_USAGE);
19310Sstevel@tonic-gate 
19320Sstevel@tonic-gate 	mdb_printf("%-?s %12s %s\n", "BUFCTL", "TIMESTAMP", "CALLER");
19330Sstevel@tonic-gate 
19340Sstevel@tonic-gate 	if (mdb_pwalk(w, (mdb_walk_cb_t)allocdby_walk, NULL, addr) == -1) {
19350Sstevel@tonic-gate 		mdb_warn("can't walk '%s' for %p", w, addr);
19360Sstevel@tonic-gate 		return (DCMD_ERR);
19370Sstevel@tonic-gate 	}
19380Sstevel@tonic-gate 
19390Sstevel@tonic-gate 	return (DCMD_OK);
19400Sstevel@tonic-gate }
19410Sstevel@tonic-gate 
19420Sstevel@tonic-gate /*ARGSUSED*/
19430Sstevel@tonic-gate int
19440Sstevel@tonic-gate allocdby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
19450Sstevel@tonic-gate {
19460Sstevel@tonic-gate 	return (allocdby_common(addr, flags, "allocdby"));
19470Sstevel@tonic-gate }
19480Sstevel@tonic-gate 
19490Sstevel@tonic-gate /*ARGSUSED*/
19500Sstevel@tonic-gate int
19510Sstevel@tonic-gate freedby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
19520Sstevel@tonic-gate {
19530Sstevel@tonic-gate 	return (allocdby_common(addr, flags, "freedby"));
19540Sstevel@tonic-gate }
19550Sstevel@tonic-gate 
19560Sstevel@tonic-gate /*
19570Sstevel@tonic-gate  * Return a string describing the address in relation to the given thread's
19580Sstevel@tonic-gate  * stack.
19590Sstevel@tonic-gate  *
19600Sstevel@tonic-gate  * - If the thread state is TS_FREE, return " (inactive interrupt thread)".
19610Sstevel@tonic-gate  *
19620Sstevel@tonic-gate  * - If the address is above the stack pointer, return an empty string
19630Sstevel@tonic-gate  *   signifying that the address is active.
19640Sstevel@tonic-gate  *
19650Sstevel@tonic-gate  * - If the address is below the stack pointer, and the thread is not on proc,
19660Sstevel@tonic-gate  *   return " (below sp)".
19670Sstevel@tonic-gate  *
19680Sstevel@tonic-gate  * - If the address is below the stack pointer, and the thread is on proc,
19690Sstevel@tonic-gate  *   return " (possibly below sp)".  Depending on context, we may or may not
19700Sstevel@tonic-gate  *   have an accurate t_sp.
19710Sstevel@tonic-gate  */
19720Sstevel@tonic-gate static const char *
19730Sstevel@tonic-gate stack_active(const kthread_t *t, uintptr_t addr)
19740Sstevel@tonic-gate {
19750Sstevel@tonic-gate 	uintptr_t panicstk;
19760Sstevel@tonic-gate 	GElf_Sym sym;
19770Sstevel@tonic-gate 
19780Sstevel@tonic-gate 	if (t->t_state == TS_FREE)
19790Sstevel@tonic-gate 		return (" (inactive interrupt thread)");
19800Sstevel@tonic-gate 
19810Sstevel@tonic-gate 	/*
19820Sstevel@tonic-gate 	 * Check to see if we're on the panic stack.  If so, ignore t_sp, as it
19830Sstevel@tonic-gate 	 * no longer relates to the thread's real stack.
19840Sstevel@tonic-gate 	 */
19850Sstevel@tonic-gate 	if (mdb_lookup_by_name("panic_stack", &sym) == 0) {
19860Sstevel@tonic-gate 		panicstk = (uintptr_t)sym.st_value;
19870Sstevel@tonic-gate 
19880Sstevel@tonic-gate 		if (t->t_sp >= panicstk && t->t_sp < panicstk + PANICSTKSIZE)
19890Sstevel@tonic-gate 			return ("");
19900Sstevel@tonic-gate 	}
19910Sstevel@tonic-gate 
19920Sstevel@tonic-gate 	if (addr >= t->t_sp + STACK_BIAS)
19930Sstevel@tonic-gate 		return ("");
19940Sstevel@tonic-gate 
19950Sstevel@tonic-gate 	if (t->t_state == TS_ONPROC)
19960Sstevel@tonic-gate 		return (" (possibly below sp)");
19970Sstevel@tonic-gate 
19980Sstevel@tonic-gate 	return (" (below sp)");
19990Sstevel@tonic-gate }
20000Sstevel@tonic-gate 
20010Sstevel@tonic-gate typedef struct whatis {
20020Sstevel@tonic-gate 	uintptr_t w_addr;
20030Sstevel@tonic-gate 	const kmem_cache_t *w_cache;
20040Sstevel@tonic-gate 	const vmem_t *w_vmem;
20050Sstevel@tonic-gate 	size_t w_slab_align;
20060Sstevel@tonic-gate 	int w_slab_found;
20070Sstevel@tonic-gate 	int w_found;
20080Sstevel@tonic-gate 	int w_kmem_lite_count;
20090Sstevel@tonic-gate 	uint_t w_verbose;
20100Sstevel@tonic-gate 	uint_t w_freemem;
20110Sstevel@tonic-gate 	uint_t w_all;
20120Sstevel@tonic-gate 	uint_t w_bufctl;
20130Sstevel@tonic-gate 	uint_t w_idspace;
20140Sstevel@tonic-gate } whatis_t;
20150Sstevel@tonic-gate 
20160Sstevel@tonic-gate static void
20170Sstevel@tonic-gate whatis_print_kmem(uintptr_t addr, uintptr_t baddr, whatis_t *w)
20180Sstevel@tonic-gate {
20190Sstevel@tonic-gate 	/* LINTED pointer cast may result in improper alignment */
20200Sstevel@tonic-gate 	uintptr_t btaddr = (uintptr_t)KMEM_BUFTAG(w->w_cache, addr);
20210Sstevel@tonic-gate 	intptr_t stat;
20220Sstevel@tonic-gate 	int count = 0;
20230Sstevel@tonic-gate 	int i;
20240Sstevel@tonic-gate 	pc_t callers[16];
20250Sstevel@tonic-gate 
20260Sstevel@tonic-gate 	if (w->w_cache->cache_flags & KMF_REDZONE) {
20270Sstevel@tonic-gate 		kmem_buftag_t bt;
20280Sstevel@tonic-gate 
20290Sstevel@tonic-gate 		if (mdb_vread(&bt, sizeof (bt), btaddr) == -1)
20300Sstevel@tonic-gate 			goto done;
20310Sstevel@tonic-gate 
20320Sstevel@tonic-gate 		stat = (intptr_t)bt.bt_bufctl ^ bt.bt_bxstat;
20330Sstevel@tonic-gate 
20340Sstevel@tonic-gate 		if (stat != KMEM_BUFTAG_ALLOC && stat != KMEM_BUFTAG_FREE)
20350Sstevel@tonic-gate 			goto done;
20360Sstevel@tonic-gate 
20370Sstevel@tonic-gate 		/*
20380Sstevel@tonic-gate 		 * provide the bufctl ptr if it has useful information
20390Sstevel@tonic-gate 		 */
20400Sstevel@tonic-gate 		if (baddr == 0 && (w->w_cache->cache_flags & KMF_AUDIT))
20410Sstevel@tonic-gate 			baddr = (uintptr_t)bt.bt_bufctl;
20420Sstevel@tonic-gate 
20430Sstevel@tonic-gate 		if (w->w_cache->cache_flags & KMF_LITE) {
20440Sstevel@tonic-gate 			count = w->w_kmem_lite_count;
20450Sstevel@tonic-gate 
20460Sstevel@tonic-gate 			if (count * sizeof (pc_t) > sizeof (callers))
20470Sstevel@tonic-gate 				count = 0;
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate 			if (count > 0 &&
20500Sstevel@tonic-gate 			    mdb_vread(callers, count * sizeof (pc_t),
20510Sstevel@tonic-gate 			    btaddr +
20520Sstevel@tonic-gate 			    offsetof(kmem_buftag_lite_t, bt_history)) == -1)
20530Sstevel@tonic-gate 				count = 0;
20540Sstevel@tonic-gate 
20550Sstevel@tonic-gate 			/*
20560Sstevel@tonic-gate 			 * skip unused callers
20570Sstevel@tonic-gate 			 */
20580Sstevel@tonic-gate 			while (count > 0 && callers[count - 1] ==
20590Sstevel@tonic-gate 			    (pc_t)KMEM_UNINITIALIZED_PATTERN)
20600Sstevel@tonic-gate 				count--;
20610Sstevel@tonic-gate 		}
20620Sstevel@tonic-gate 	}
20630Sstevel@tonic-gate 
20640Sstevel@tonic-gate done:
20650Sstevel@tonic-gate 	if (baddr == 0)
20660Sstevel@tonic-gate 		mdb_printf("%p is %p+%p, %s from %s\n",
20670Sstevel@tonic-gate 		    w->w_addr, addr, w->w_addr - addr,
20680Sstevel@tonic-gate 		    w->w_freemem == FALSE ? "allocated" : "freed",
20690Sstevel@tonic-gate 		    w->w_cache->cache_name);
20700Sstevel@tonic-gate 	else
20710Sstevel@tonic-gate 		mdb_printf("%p is %p+%p, bufctl %p %s from %s\n",
20720Sstevel@tonic-gate 		    w->w_addr, addr, w->w_addr - addr, baddr,
20730Sstevel@tonic-gate 		    w->w_freemem == FALSE ? "allocated" : "freed",
20740Sstevel@tonic-gate 		    w->w_cache->cache_name);
20750Sstevel@tonic-gate 
20760Sstevel@tonic-gate 	if (count > 0) {
20770Sstevel@tonic-gate 		mdb_inc_indent(8);
20780Sstevel@tonic-gate 		mdb_printf("recent caller%s: %a%s", (count != 1)? "s":"",
20790Sstevel@tonic-gate 		    callers[0], (count != 1)? ", ":"\n");
20800Sstevel@tonic-gate 		for (i = 1; i < count; i++)
20810Sstevel@tonic-gate 			mdb_printf("%a%s", callers[i],
20820Sstevel@tonic-gate 			    (i + 1 < count)? ", ":"\n");
20830Sstevel@tonic-gate 		mdb_dec_indent(8);
20840Sstevel@tonic-gate 	}
20850Sstevel@tonic-gate }
20860Sstevel@tonic-gate 
20870Sstevel@tonic-gate /*ARGSUSED*/
20880Sstevel@tonic-gate static int
20890Sstevel@tonic-gate whatis_walk_kmem(uintptr_t addr, void *ignored, whatis_t *w)
20900Sstevel@tonic-gate {
20910Sstevel@tonic-gate 	if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize)
20920Sstevel@tonic-gate 		return (WALK_NEXT);
20930Sstevel@tonic-gate 
20940Sstevel@tonic-gate 	whatis_print_kmem(addr, 0, w);
20950Sstevel@tonic-gate 	w->w_found++;
20960Sstevel@tonic-gate 	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
20970Sstevel@tonic-gate }
20980Sstevel@tonic-gate 
20990Sstevel@tonic-gate static int
21000Sstevel@tonic-gate whatis_walk_seg(uintptr_t addr, const vmem_seg_t *vs, whatis_t *w)
21010Sstevel@tonic-gate {
21020Sstevel@tonic-gate 	if (w->w_addr < vs->vs_start || w->w_addr >= vs->vs_end)
21030Sstevel@tonic-gate 		return (WALK_NEXT);
21040Sstevel@tonic-gate 
21050Sstevel@tonic-gate 	mdb_printf("%p is %p+%p ", w->w_addr,
21060Sstevel@tonic-gate 	    vs->vs_start, w->w_addr - vs->vs_start);
21070Sstevel@tonic-gate 
21080Sstevel@tonic-gate 	/*
21090Sstevel@tonic-gate 	 * Always provide the vmem_seg pointer if it has a stack trace.
21100Sstevel@tonic-gate 	 */
21110Sstevel@tonic-gate 	if (w->w_bufctl == TRUE ||
21120Sstevel@tonic-gate 	    (vs->vs_type == VMEM_ALLOC && vs->vs_depth != 0)) {
21130Sstevel@tonic-gate 		mdb_printf("(vmem_seg %p) ", addr);
21140Sstevel@tonic-gate 	}
21150Sstevel@tonic-gate 
21160Sstevel@tonic-gate 	mdb_printf("%sfrom %s vmem arena\n", w->w_freemem == TRUE ?
21170Sstevel@tonic-gate 	    "freed " : "", w->w_vmem->vm_name);
21180Sstevel@tonic-gate 
21190Sstevel@tonic-gate 	w->w_found++;
21200Sstevel@tonic-gate 	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
21210Sstevel@tonic-gate }
21220Sstevel@tonic-gate 
21230Sstevel@tonic-gate static int
21240Sstevel@tonic-gate whatis_walk_vmem(uintptr_t addr, const vmem_t *vmem, whatis_t *w)
21250Sstevel@tonic-gate {
21260Sstevel@tonic-gate 	const char *nm = vmem->vm_name;
21270Sstevel@tonic-gate 	w->w_vmem = vmem;
21280Sstevel@tonic-gate 	w->w_freemem = FALSE;
21290Sstevel@tonic-gate 
21300Sstevel@tonic-gate 	if (((vmem->vm_cflags & VMC_IDENTIFIER) != 0) ^ w->w_idspace)
21310Sstevel@tonic-gate 		return (WALK_NEXT);
21320Sstevel@tonic-gate 
21330Sstevel@tonic-gate 	if (w->w_verbose)
21340Sstevel@tonic-gate 		mdb_printf("Searching vmem arena %s...\n", nm);
21350Sstevel@tonic-gate 
21360Sstevel@tonic-gate 	if (mdb_pwalk("vmem_alloc",
21370Sstevel@tonic-gate 	    (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) {
21380Sstevel@tonic-gate 		mdb_warn("can't walk vmem seg for %p", addr);
21390Sstevel@tonic-gate 		return (WALK_NEXT);
21400Sstevel@tonic-gate 	}
21410Sstevel@tonic-gate 
21420Sstevel@tonic-gate 	if (w->w_found && w->w_all == FALSE)
21430Sstevel@tonic-gate 		return (WALK_DONE);
21440Sstevel@tonic-gate 
21450Sstevel@tonic-gate 	if (w->w_verbose)
21460Sstevel@tonic-gate 		mdb_printf("Searching vmem arena %s for free virtual...\n", nm);
21470Sstevel@tonic-gate 
21480Sstevel@tonic-gate 	w->w_freemem = TRUE;
21490Sstevel@tonic-gate 
21500Sstevel@tonic-gate 	if (mdb_pwalk("vmem_free",
21510Sstevel@tonic-gate 	    (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) {
21520Sstevel@tonic-gate 		mdb_warn("can't walk vmem seg for %p", addr);
21530Sstevel@tonic-gate 		return (WALK_NEXT);
21540Sstevel@tonic-gate 	}
21550Sstevel@tonic-gate 
21560Sstevel@tonic-gate 	return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT);
21570Sstevel@tonic-gate }
21580Sstevel@tonic-gate 
21590Sstevel@tonic-gate /*ARGSUSED*/
21600Sstevel@tonic-gate static int
21610Sstevel@tonic-gate whatis_walk_bufctl(uintptr_t baddr, const kmem_bufctl_t *bcp, whatis_t *w)
21620Sstevel@tonic-gate {
21630Sstevel@tonic-gate 	uintptr_t addr;
21640Sstevel@tonic-gate 
21650Sstevel@tonic-gate 	if (bcp == NULL)
21660Sstevel@tonic-gate 		return (WALK_NEXT);
21670Sstevel@tonic-gate 
21680Sstevel@tonic-gate 	addr = (uintptr_t)bcp->bc_addr;
21690Sstevel@tonic-gate 
21700Sstevel@tonic-gate 	if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize)
21710Sstevel@tonic-gate 		return (WALK_NEXT);
21720Sstevel@tonic-gate 
21730Sstevel@tonic-gate 	whatis_print_kmem(addr, baddr, w);
21740Sstevel@tonic-gate 	w->w_found++;
21750Sstevel@tonic-gate 	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
21760Sstevel@tonic-gate }
21770Sstevel@tonic-gate 
21780Sstevel@tonic-gate /*ARGSUSED*/
21790Sstevel@tonic-gate static int
21800Sstevel@tonic-gate whatis_walk_slab(uintptr_t saddr, const kmem_slab_t *sp, whatis_t *w)
21810Sstevel@tonic-gate {
21820Sstevel@tonic-gate 	uintptr_t base = P2ALIGN((uintptr_t)sp->slab_base, w->w_slab_align);
21830Sstevel@tonic-gate 
21840Sstevel@tonic-gate 	if ((w->w_addr - base) >= w->w_cache->cache_slabsize)
21850Sstevel@tonic-gate 		return (WALK_NEXT);
21860Sstevel@tonic-gate 
21870Sstevel@tonic-gate 	w->w_slab_found++;
21880Sstevel@tonic-gate 	return (WALK_DONE);
21890Sstevel@tonic-gate }
21900Sstevel@tonic-gate 
21910Sstevel@tonic-gate static int
21920Sstevel@tonic-gate whatis_walk_cache(uintptr_t addr, const kmem_cache_t *c, whatis_t *w)
21930Sstevel@tonic-gate {
21940Sstevel@tonic-gate 	char *walk, *freewalk;
21950Sstevel@tonic-gate 	mdb_walk_cb_t func;
21960Sstevel@tonic-gate 	vmem_t *vmp = c->cache_arena;
21970Sstevel@tonic-gate 
21980Sstevel@tonic-gate 	if (((c->cache_flags & VMC_IDENTIFIER) != 0) ^ w->w_idspace)
21990Sstevel@tonic-gate 		return (WALK_NEXT);
22000Sstevel@tonic-gate 
22010Sstevel@tonic-gate 	if (w->w_bufctl == FALSE) {
22020Sstevel@tonic-gate 		walk = "kmem";
22030Sstevel@tonic-gate 		freewalk = "freemem";
22040Sstevel@tonic-gate 		func = (mdb_walk_cb_t)whatis_walk_kmem;
22050Sstevel@tonic-gate 	} else {
22060Sstevel@tonic-gate 		walk = "bufctl";
22070Sstevel@tonic-gate 		freewalk = "freectl";
22080Sstevel@tonic-gate 		func = (mdb_walk_cb_t)whatis_walk_bufctl;
22090Sstevel@tonic-gate 	}
22100Sstevel@tonic-gate 
22110Sstevel@tonic-gate 	w->w_cache = c;
22120Sstevel@tonic-gate 
22130Sstevel@tonic-gate 	if (w->w_verbose)
22140Sstevel@tonic-gate 		mdb_printf("Searching %s's slabs...\n", c->cache_name);
22150Sstevel@tonic-gate 
22160Sstevel@tonic-gate 	/*
22170Sstevel@tonic-gate 	 * Verify that the address is in one of the cache's slabs.  If not,
22180Sstevel@tonic-gate 	 * we can skip the more expensive walkers.  (this is purely a
22190Sstevel@tonic-gate 	 * heuristic -- as long as there are no false-negatives, we'll be fine)
22200Sstevel@tonic-gate 	 *
22210Sstevel@tonic-gate 	 * We try to get the cache's arena's quantum, since to accurately
22220Sstevel@tonic-gate 	 * get the base of a slab, you have to align it to the quantum.  If
22230Sstevel@tonic-gate 	 * it doesn't look sensible, we fall back to not aligning.
22240Sstevel@tonic-gate 	 */
22250Sstevel@tonic-gate 	if (mdb_vread(&w->w_slab_align, sizeof (w->w_slab_align),
22260Sstevel@tonic-gate 	    (uintptr_t)&vmp->vm_quantum) == -1) {
22270Sstevel@tonic-gate 		mdb_warn("unable to read %p->cache_arena->vm_quantum", c);
22280Sstevel@tonic-gate 		w->w_slab_align = 1;
22290Sstevel@tonic-gate 	}
22300Sstevel@tonic-gate 
22310Sstevel@tonic-gate 	if ((c->cache_slabsize < w->w_slab_align) || w->w_slab_align == 0 ||
22320Sstevel@tonic-gate 	    (w->w_slab_align & (w->w_slab_align - 1))) {
22330Sstevel@tonic-gate 		mdb_warn("%p's arena has invalid quantum (0x%p)\n", c,
22340Sstevel@tonic-gate 		    w->w_slab_align);
22350Sstevel@tonic-gate 		w->w_slab_align = 1;
22360Sstevel@tonic-gate 	}
22370Sstevel@tonic-gate 
22380Sstevel@tonic-gate 	w->w_slab_found = 0;
22390Sstevel@tonic-gate 	if (mdb_pwalk("kmem_slab", (mdb_walk_cb_t)whatis_walk_slab, w,
22400Sstevel@tonic-gate 	    addr) == -1) {
22410Sstevel@tonic-gate 		mdb_warn("can't find kmem_slab walker");
22420Sstevel@tonic-gate 		return (WALK_DONE);
22430Sstevel@tonic-gate 	}
22440Sstevel@tonic-gate 	if (w->w_slab_found == 0)
22450Sstevel@tonic-gate 		return (WALK_NEXT);
22460Sstevel@tonic-gate 
22470Sstevel@tonic-gate 	if (c->cache_flags & KMF_LITE) {
22480Sstevel@tonic-gate 		if (mdb_readvar(&w->w_kmem_lite_count,
22490Sstevel@tonic-gate 		    "kmem_lite_count") == -1 || w->w_kmem_lite_count > 16)
22500Sstevel@tonic-gate 			w->w_kmem_lite_count = 0;
22510Sstevel@tonic-gate 	}
22520Sstevel@tonic-gate 
22530Sstevel@tonic-gate 	if (w->w_verbose)
22540Sstevel@tonic-gate 		mdb_printf("Searching %s...\n", c->cache_name);
22550Sstevel@tonic-gate 
22560Sstevel@tonic-gate 	w->w_freemem = FALSE;
22570Sstevel@tonic-gate 
22580Sstevel@tonic-gate 	if (mdb_pwalk(walk, func, w, addr) == -1) {
22590Sstevel@tonic-gate 		mdb_warn("can't find %s walker", walk);
22600Sstevel@tonic-gate 		return (WALK_DONE);
22610Sstevel@tonic-gate 	}
22620Sstevel@tonic-gate 
22630Sstevel@tonic-gate 	if (w->w_found && w->w_all == FALSE)
22640Sstevel@tonic-gate 		return (WALK_DONE);
22650Sstevel@tonic-gate 
22660Sstevel@tonic-gate 	/*
22670Sstevel@tonic-gate 	 * We have searched for allocated memory; now search for freed memory.
22680Sstevel@tonic-gate 	 */
22690Sstevel@tonic-gate 	if (w->w_verbose)
22700Sstevel@tonic-gate 		mdb_printf("Searching %s for free memory...\n", c->cache_name);
22710Sstevel@tonic-gate 
22720Sstevel@tonic-gate 	w->w_freemem = TRUE;
22730Sstevel@tonic-gate 
22740Sstevel@tonic-gate 	if (mdb_pwalk(freewalk, func, w, addr) == -1) {
22750Sstevel@tonic-gate 		mdb_warn("can't find %s walker", freewalk);
22760Sstevel@tonic-gate 		return (WALK_DONE);
22770Sstevel@tonic-gate 	}
22780Sstevel@tonic-gate 
22790Sstevel@tonic-gate 	return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT);
22800Sstevel@tonic-gate }
22810Sstevel@tonic-gate 
22820Sstevel@tonic-gate static int
22830Sstevel@tonic-gate whatis_walk_touch(uintptr_t addr, const kmem_cache_t *c, whatis_t *w)
22840Sstevel@tonic-gate {
22850Sstevel@tonic-gate 	if (c->cache_cflags & KMC_NOTOUCH)
22860Sstevel@tonic-gate 		return (WALK_NEXT);
22870Sstevel@tonic-gate 
22880Sstevel@tonic-gate 	return (whatis_walk_cache(addr, c, w));
22890Sstevel@tonic-gate }
22900Sstevel@tonic-gate 
22910Sstevel@tonic-gate static int
22920Sstevel@tonic-gate whatis_walk_notouch(uintptr_t addr, const kmem_cache_t *c, whatis_t *w)
22930Sstevel@tonic-gate {
22940Sstevel@tonic-gate 	if (!(c->cache_cflags & KMC_NOTOUCH))
22950Sstevel@tonic-gate 		return (WALK_NEXT);
22960Sstevel@tonic-gate 
22970Sstevel@tonic-gate 	return (whatis_walk_cache(addr, c, w));
22980Sstevel@tonic-gate }
22990Sstevel@tonic-gate 
23000Sstevel@tonic-gate static int
23010Sstevel@tonic-gate whatis_walk_thread(uintptr_t addr, const kthread_t *t, whatis_t *w)
23020Sstevel@tonic-gate {
23030Sstevel@tonic-gate 	/*
23040Sstevel@tonic-gate 	 * Often, one calls ::whatis on an address from a thread structure.
23050Sstevel@tonic-gate 	 * We use this opportunity to short circuit this case...
23060Sstevel@tonic-gate 	 */
23070Sstevel@tonic-gate 	if (w->w_addr >= addr && w->w_addr < addr + sizeof (kthread_t)) {
23080Sstevel@tonic-gate 		mdb_printf("%p is %p+%p, allocated as a thread structure\n",
23090Sstevel@tonic-gate 		    w->w_addr, addr, w->w_addr - addr);
23100Sstevel@tonic-gate 		w->w_found++;
23110Sstevel@tonic-gate 		return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
23120Sstevel@tonic-gate 	}
23130Sstevel@tonic-gate 
23140Sstevel@tonic-gate 	if (w->w_addr < (uintptr_t)t->t_stkbase ||
23150Sstevel@tonic-gate 	    w->w_addr > (uintptr_t)t->t_stk)
23160Sstevel@tonic-gate 		return (WALK_NEXT);
23170Sstevel@tonic-gate 
23180Sstevel@tonic-gate 	if (t->t_stkbase == NULL)
23190Sstevel@tonic-gate 		return (WALK_NEXT);
23200Sstevel@tonic-gate 
23210Sstevel@tonic-gate 	mdb_printf("%p is in thread %p's stack%s\n", w->w_addr, addr,
23220Sstevel@tonic-gate 	    stack_active(t, w->w_addr));
23230Sstevel@tonic-gate 
23240Sstevel@tonic-gate 	w->w_found++;
23250Sstevel@tonic-gate 	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
23260Sstevel@tonic-gate }
23270Sstevel@tonic-gate 
23280Sstevel@tonic-gate static int
23290Sstevel@tonic-gate whatis_walk_modctl(uintptr_t addr, const struct modctl *m, whatis_t *w)
23300Sstevel@tonic-gate {
23310Sstevel@tonic-gate 	struct module mod;
23320Sstevel@tonic-gate 	char name[MODMAXNAMELEN], *where;
23330Sstevel@tonic-gate 	char c[MDB_SYM_NAMLEN];
23340Sstevel@tonic-gate 	Shdr shdr;
23350Sstevel@tonic-gate 	GElf_Sym sym;
23360Sstevel@tonic-gate 
23370Sstevel@tonic-gate 	if (m->mod_mp == NULL)
23380Sstevel@tonic-gate 		return (WALK_NEXT);
23390Sstevel@tonic-gate 
23400Sstevel@tonic-gate 	if (mdb_vread(&mod, sizeof (mod), (uintptr_t)m->mod_mp) == -1) {
23410Sstevel@tonic-gate 		mdb_warn("couldn't read modctl %p's module", addr);
23420Sstevel@tonic-gate 		return (WALK_NEXT);
23430Sstevel@tonic-gate 	}
23440Sstevel@tonic-gate 
23450Sstevel@tonic-gate 	if (w->w_addr >= (uintptr_t)mod.text &&
23460Sstevel@tonic-gate 	    w->w_addr < (uintptr_t)mod.text + mod.text_size) {
23470Sstevel@tonic-gate 		where = "text segment";
23480Sstevel@tonic-gate 		goto found;
23490Sstevel@tonic-gate 	}
23500Sstevel@tonic-gate 
23510Sstevel@tonic-gate 	if (w->w_addr >= (uintptr_t)mod.data &&
23520Sstevel@tonic-gate 	    w->w_addr < (uintptr_t)mod.data + mod.data_size) {
23530Sstevel@tonic-gate 		where = "data segment";
23540Sstevel@tonic-gate 		goto found;
23550Sstevel@tonic-gate 	}
23560Sstevel@tonic-gate 
23570Sstevel@tonic-gate 	if (w->w_addr >= (uintptr_t)mod.bss &&
23580Sstevel@tonic-gate 	    w->w_addr < (uintptr_t)mod.bss + mod.bss_size) {
23590Sstevel@tonic-gate 		where = "bss";
23600Sstevel@tonic-gate 		goto found;
23610Sstevel@tonic-gate 	}
23620Sstevel@tonic-gate 
23630Sstevel@tonic-gate 	if (mdb_vread(&shdr, sizeof (shdr), (uintptr_t)mod.symhdr) == -1) {
23640Sstevel@tonic-gate 		mdb_warn("couldn't read symbol header for %p's module", addr);
23650Sstevel@tonic-gate 		return (WALK_NEXT);
23660Sstevel@tonic-gate 	}
23670Sstevel@tonic-gate 
23680Sstevel@tonic-gate 	if (w->w_addr >= (uintptr_t)mod.symtbl && w->w_addr <
23690Sstevel@tonic-gate 	    (uintptr_t)mod.symtbl + (uintptr_t)mod.nsyms * shdr.sh_entsize) {
23700Sstevel@tonic-gate 		where = "symtab";
23710Sstevel@tonic-gate 		goto found;
23720Sstevel@tonic-gate 	}
23730Sstevel@tonic-gate 
23740Sstevel@tonic-gate 	if (w->w_addr >= (uintptr_t)mod.symspace &&
23750Sstevel@tonic-gate 	    w->w_addr < (uintptr_t)mod.symspace + (uintptr_t)mod.symsize) {
23760Sstevel@tonic-gate 		where = "symspace";
23770Sstevel@tonic-gate 		goto found;
23780Sstevel@tonic-gate 	}
23790Sstevel@tonic-gate 
23800Sstevel@tonic-gate 	return (WALK_NEXT);
23810Sstevel@tonic-gate 
23820Sstevel@tonic-gate found:
23830Sstevel@tonic-gate 	if (mdb_readstr(name, sizeof (name), (uintptr_t)m->mod_modname) == -1)
23840Sstevel@tonic-gate 		(void) mdb_snprintf(name, sizeof (name), "0x%p", addr);
23850Sstevel@tonic-gate 
23860Sstevel@tonic-gate 	mdb_printf("%p is ", w->w_addr);
23870Sstevel@tonic-gate 
23880Sstevel@tonic-gate 	/*
23890Sstevel@tonic-gate 	 * If we found this address in a module, then there's a chance that
23900Sstevel@tonic-gate 	 * it's actually a named symbol.  Try the symbol lookup.
23910Sstevel@tonic-gate 	 */
23920Sstevel@tonic-gate 	if (mdb_lookup_by_addr(w->w_addr, MDB_SYM_FUZZY, c, sizeof (c),
23930Sstevel@tonic-gate 	    &sym) != -1 && w->w_addr >= (uintptr_t)sym.st_value &&
23940Sstevel@tonic-gate 	    w->w_addr < (uintptr_t)sym.st_value + sym.st_size) {
23950Sstevel@tonic-gate 		mdb_printf("%s+%lx ", c, w->w_addr - (uintptr_t)sym.st_value);
23960Sstevel@tonic-gate 	}
23970Sstevel@tonic-gate 
23980Sstevel@tonic-gate 	mdb_printf("in %s's %s\n", name, where);
23990Sstevel@tonic-gate 
24000Sstevel@tonic-gate 	w->w_found++;
24010Sstevel@tonic-gate 	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
24020Sstevel@tonic-gate }
24030Sstevel@tonic-gate 
24040Sstevel@tonic-gate /*ARGSUSED*/
24050Sstevel@tonic-gate static int
24060Sstevel@tonic-gate whatis_walk_page(uintptr_t addr, const void *ignored, whatis_t *w)
24070Sstevel@tonic-gate {
24080Sstevel@tonic-gate 	static int machsize = 0;
24090Sstevel@tonic-gate 	mdb_ctf_id_t id;
24100Sstevel@tonic-gate 
24110Sstevel@tonic-gate 	if (machsize == 0) {
24120Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name("unix`page_t", &id) == 0)
24130Sstevel@tonic-gate 			machsize = mdb_ctf_type_size(id);
24140Sstevel@tonic-gate 		else {
24150Sstevel@tonic-gate 			mdb_warn("could not get size of page_t");
24160Sstevel@tonic-gate 			machsize = sizeof (page_t);
24170Sstevel@tonic-gate 		}
24180Sstevel@tonic-gate 	}
24190Sstevel@tonic-gate 
24200Sstevel@tonic-gate 	if (w->w_addr < addr || w->w_addr >= addr + machsize)
24210Sstevel@tonic-gate 		return (WALK_NEXT);
24220Sstevel@tonic-gate 
24230Sstevel@tonic-gate 	mdb_printf("%p is %p+%p, allocated as a page structure\n",
24240Sstevel@tonic-gate 	    w->w_addr, addr, w->w_addr - addr);
24250Sstevel@tonic-gate 
24260Sstevel@tonic-gate 	w->w_found++;
24270Sstevel@tonic-gate 	return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE);
24280Sstevel@tonic-gate }
24290Sstevel@tonic-gate 
24300Sstevel@tonic-gate int
24310Sstevel@tonic-gate whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
24320Sstevel@tonic-gate {
24330Sstevel@tonic-gate 	whatis_t w;
24340Sstevel@tonic-gate 
24350Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
24360Sstevel@tonic-gate 		return (DCMD_USAGE);
24370Sstevel@tonic-gate 
24380Sstevel@tonic-gate 	w.w_verbose = FALSE;
24390Sstevel@tonic-gate 	w.w_bufctl = FALSE;
24400Sstevel@tonic-gate 	w.w_all = FALSE;
24410Sstevel@tonic-gate 	w.w_idspace = FALSE;
24420Sstevel@tonic-gate 
24430Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
24440Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &w.w_verbose,
24450Sstevel@tonic-gate 	    'a', MDB_OPT_SETBITS, TRUE, &w.w_all,
24460Sstevel@tonic-gate 	    'i', MDB_OPT_SETBITS, TRUE, &w.w_idspace,
24470Sstevel@tonic-gate 	    'b', MDB_OPT_SETBITS, TRUE, &w.w_bufctl, NULL) != argc)
24480Sstevel@tonic-gate 		return (DCMD_USAGE);
24490Sstevel@tonic-gate 
24500Sstevel@tonic-gate 	w.w_addr = addr;
24510Sstevel@tonic-gate 	w.w_found = 0;
24520Sstevel@tonic-gate 
24530Sstevel@tonic-gate 	if (w.w_verbose)
24540Sstevel@tonic-gate 		mdb_printf("Searching modules...\n");
24550Sstevel@tonic-gate 
24560Sstevel@tonic-gate 	if (!w.w_idspace) {
24570Sstevel@tonic-gate 		if (mdb_walk("modctl", (mdb_walk_cb_t)whatis_walk_modctl, &w)
24580Sstevel@tonic-gate 		    == -1) {
24590Sstevel@tonic-gate 			mdb_warn("couldn't find modctl walker");
24600Sstevel@tonic-gate 			return (DCMD_ERR);
24610Sstevel@tonic-gate 		}
24620Sstevel@tonic-gate 
24630Sstevel@tonic-gate 		if (w.w_found && w.w_all == FALSE)
24640Sstevel@tonic-gate 			return (DCMD_OK);
24650Sstevel@tonic-gate 
24660Sstevel@tonic-gate 		/*
24670Sstevel@tonic-gate 		 * Now search all thread stacks.  Yes, this is a little weak; we
24680Sstevel@tonic-gate 		 * can save a lot of work by first checking to see if the
24690Sstevel@tonic-gate 		 * address is in segkp vs. segkmem.  But hey, computers are
24700Sstevel@tonic-gate 		 * fast.
24710Sstevel@tonic-gate 		 */
24720Sstevel@tonic-gate 		if (w.w_verbose)
24730Sstevel@tonic-gate 			mdb_printf("Searching threads...\n");
24740Sstevel@tonic-gate 
24750Sstevel@tonic-gate 		if (mdb_walk("thread", (mdb_walk_cb_t)whatis_walk_thread, &w)
24760Sstevel@tonic-gate 		    == -1) {
24770Sstevel@tonic-gate 			mdb_warn("couldn't find thread walker");
24780Sstevel@tonic-gate 			return (DCMD_ERR);
24790Sstevel@tonic-gate 		}
24800Sstevel@tonic-gate 
24810Sstevel@tonic-gate 		if (w.w_found && w.w_all == FALSE)
24820Sstevel@tonic-gate 			return (DCMD_OK);
24830Sstevel@tonic-gate 
24840Sstevel@tonic-gate 		if (w.w_verbose)
24850Sstevel@tonic-gate 			mdb_printf("Searching page structures...\n");
24860Sstevel@tonic-gate 
24870Sstevel@tonic-gate 		if (mdb_walk("page", (mdb_walk_cb_t)whatis_walk_page, &w)
24880Sstevel@tonic-gate 		    == -1) {
24890Sstevel@tonic-gate 			mdb_warn("couldn't find page walker");
24900Sstevel@tonic-gate 			return (DCMD_ERR);
24910Sstevel@tonic-gate 		}
24920Sstevel@tonic-gate 
24930Sstevel@tonic-gate 		if (w.w_found && w.w_all == FALSE)
24940Sstevel@tonic-gate 			return (DCMD_OK);
24950Sstevel@tonic-gate 	}
24960Sstevel@tonic-gate 
24970Sstevel@tonic-gate 	if (mdb_walk("kmem_cache",
24980Sstevel@tonic-gate 	    (mdb_walk_cb_t)whatis_walk_touch, &w) == -1) {
24990Sstevel@tonic-gate 		mdb_warn("couldn't find kmem_cache walker");
25000Sstevel@tonic-gate 		return (DCMD_ERR);
25010Sstevel@tonic-gate 	}
25020Sstevel@tonic-gate 
25030Sstevel@tonic-gate 	if (w.w_found && w.w_all == FALSE)
25040Sstevel@tonic-gate 		return (DCMD_OK);
25050Sstevel@tonic-gate 
25060Sstevel@tonic-gate 	if (mdb_walk("kmem_cache",
25070Sstevel@tonic-gate 	    (mdb_walk_cb_t)whatis_walk_notouch, &w) == -1) {
25080Sstevel@tonic-gate 		mdb_warn("couldn't find kmem_cache walker");
25090Sstevel@tonic-gate 		return (DCMD_ERR);
25100Sstevel@tonic-gate 	}
25110Sstevel@tonic-gate 
25120Sstevel@tonic-gate 	if (w.w_found && w.w_all == FALSE)
25130Sstevel@tonic-gate 		return (DCMD_OK);
25140Sstevel@tonic-gate 
25150Sstevel@tonic-gate 	if (mdb_walk("vmem_postfix",
25160Sstevel@tonic-gate 	    (mdb_walk_cb_t)whatis_walk_vmem, &w) == -1) {
25170Sstevel@tonic-gate 		mdb_warn("couldn't find vmem_postfix walker");
25180Sstevel@tonic-gate 		return (DCMD_ERR);
25190Sstevel@tonic-gate 	}
25200Sstevel@tonic-gate 
25210Sstevel@tonic-gate 	if (w.w_found == 0)
25220Sstevel@tonic-gate 		mdb_printf("%p is unknown\n", addr);
25230Sstevel@tonic-gate 
25240Sstevel@tonic-gate 	return (DCMD_OK);
25250Sstevel@tonic-gate }
25260Sstevel@tonic-gate 
25270Sstevel@tonic-gate void
25280Sstevel@tonic-gate whatis_help(void)
25290Sstevel@tonic-gate {
25300Sstevel@tonic-gate 	mdb_printf(
25310Sstevel@tonic-gate 	    "Given a virtual address, attempt to determine where it came\n"
25320Sstevel@tonic-gate 	    "from.\n"
25330Sstevel@tonic-gate 	    "\n"
25340Sstevel@tonic-gate 	    "\t-v\tVerbose output; display caches/arenas/etc as they are\n"
25350Sstevel@tonic-gate 	    "\t\tsearched\n"
25360Sstevel@tonic-gate 	    "\t-a\tFind all possible sources.  Default behavior is to stop at\n"
25370Sstevel@tonic-gate 	    "\t\tthe first (most specific) source.\n"
25380Sstevel@tonic-gate 	    "\t-i\tSearch only identifier arenas and caches.  By default\n"
25390Sstevel@tonic-gate 	    "\t\tthese are ignored.\n"
25400Sstevel@tonic-gate 	    "\t-b\tReport bufctls and vmem_segs for matches in kmem and vmem,\n"
25410Sstevel@tonic-gate 	    "\t\trespectively.  Warning: if the buffer exists, but does not\n"
25420Sstevel@tonic-gate 	    "\t\thave a bufctl, it will not be reported.\n");
25430Sstevel@tonic-gate }
25440Sstevel@tonic-gate 
25450Sstevel@tonic-gate typedef struct kmem_log_cpu {
25460Sstevel@tonic-gate 	uintptr_t kmc_low;
25470Sstevel@tonic-gate 	uintptr_t kmc_high;
25480Sstevel@tonic-gate } kmem_log_cpu_t;
25490Sstevel@tonic-gate 
25500Sstevel@tonic-gate typedef struct kmem_log_data {
25510Sstevel@tonic-gate 	uintptr_t kmd_addr;
25520Sstevel@tonic-gate 	kmem_log_cpu_t *kmd_cpu;
25530Sstevel@tonic-gate } kmem_log_data_t;
25540Sstevel@tonic-gate 
25550Sstevel@tonic-gate int
25560Sstevel@tonic-gate kmem_log_walk(uintptr_t addr, const kmem_bufctl_audit_t *b,
25570Sstevel@tonic-gate     kmem_log_data_t *kmd)
25580Sstevel@tonic-gate {
25590Sstevel@tonic-gate 	int i;
25600Sstevel@tonic-gate 	kmem_log_cpu_t *kmc = kmd->kmd_cpu;
25610Sstevel@tonic-gate 	size_t bufsize;
25620Sstevel@tonic-gate 
25630Sstevel@tonic-gate 	for (i = 0; i < NCPU; i++) {
25640Sstevel@tonic-gate 		if (addr >= kmc[i].kmc_low && addr < kmc[i].kmc_high)
25650Sstevel@tonic-gate 			break;
25660Sstevel@tonic-gate 	}
25670Sstevel@tonic-gate 
25680Sstevel@tonic-gate 	if (kmd->kmd_addr) {
25690Sstevel@tonic-gate 		if (b->bc_cache == NULL)
25700Sstevel@tonic-gate 			return (WALK_NEXT);
25710Sstevel@tonic-gate 
25720Sstevel@tonic-gate 		if (mdb_vread(&bufsize, sizeof (bufsize),
25730Sstevel@tonic-gate 		    (uintptr_t)&b->bc_cache->cache_bufsize) == -1) {
25740Sstevel@tonic-gate 			mdb_warn(
25750Sstevel@tonic-gate 			    "failed to read cache_bufsize for cache at %p",
25760Sstevel@tonic-gate 			    b->bc_cache);
25770Sstevel@tonic-gate 			return (WALK_ERR);
25780Sstevel@tonic-gate 		}
25790Sstevel@tonic-gate 
25800Sstevel@tonic-gate 		if (kmd->kmd_addr < (uintptr_t)b->bc_addr ||
25810Sstevel@tonic-gate 		    kmd->kmd_addr >= (uintptr_t)b->bc_addr + bufsize)
25820Sstevel@tonic-gate 			return (WALK_NEXT);
25830Sstevel@tonic-gate 	}
25840Sstevel@tonic-gate 
25850Sstevel@tonic-gate 	if (i == NCPU)
25860Sstevel@tonic-gate 		mdb_printf("   ");
25870Sstevel@tonic-gate 	else
25880Sstevel@tonic-gate 		mdb_printf("%3d", i);
25890Sstevel@tonic-gate 
25900Sstevel@tonic-gate 	mdb_printf(" %0?p %0?p %16llx %0?p\n", addr, b->bc_addr,
25910Sstevel@tonic-gate 	    b->bc_timestamp, b->bc_thread);
25920Sstevel@tonic-gate 
25930Sstevel@tonic-gate 	return (WALK_NEXT);
25940Sstevel@tonic-gate }
25950Sstevel@tonic-gate 
25960Sstevel@tonic-gate /*ARGSUSED*/
25970Sstevel@tonic-gate int
25980Sstevel@tonic-gate kmem_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
25990Sstevel@tonic-gate {
26000Sstevel@tonic-gate 	kmem_log_header_t lh;
26010Sstevel@tonic-gate 	kmem_cpu_log_header_t clh;
26020Sstevel@tonic-gate 	uintptr_t lhp, clhp;
26030Sstevel@tonic-gate 	int ncpus;
26040Sstevel@tonic-gate 	uintptr_t *cpu;
26050Sstevel@tonic-gate 	GElf_Sym sym;
26060Sstevel@tonic-gate 	kmem_log_cpu_t *kmc;
26070Sstevel@tonic-gate 	int i;
26080Sstevel@tonic-gate 	kmem_log_data_t kmd;
26090Sstevel@tonic-gate 	uint_t opt_b = FALSE;
26100Sstevel@tonic-gate 
26110Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
26120Sstevel@tonic-gate 	    'b', MDB_OPT_SETBITS, TRUE, &opt_b, NULL) != argc)
26130Sstevel@tonic-gate 		return (DCMD_USAGE);
26140Sstevel@tonic-gate 
26150Sstevel@tonic-gate 	if (mdb_readvar(&lhp, "kmem_transaction_log") == -1) {
26160Sstevel@tonic-gate 		mdb_warn("failed to read 'kmem_transaction_log'");
26170Sstevel@tonic-gate 		return (DCMD_ERR);
26180Sstevel@tonic-gate 	}
26190Sstevel@tonic-gate 
26200Sstevel@tonic-gate 	if (lhp == NULL) {
26210Sstevel@tonic-gate 		mdb_warn("no kmem transaction log\n");
26220Sstevel@tonic-gate 		return (DCMD_ERR);
26230Sstevel@tonic-gate 	}
26240Sstevel@tonic-gate 
26250Sstevel@tonic-gate 	mdb_readvar(&ncpus, "ncpus");
26260Sstevel@tonic-gate 
26270Sstevel@tonic-gate 	if (mdb_vread(&lh, sizeof (kmem_log_header_t), lhp) == -1) {
26280Sstevel@tonic-gate 		mdb_warn("failed to read log header at %p", lhp);
26290Sstevel@tonic-gate 		return (DCMD_ERR);
26300Sstevel@tonic-gate 	}
26310Sstevel@tonic-gate 
26320Sstevel@tonic-gate 	clhp = lhp + ((uintptr_t)&lh.lh_cpu[0] - (uintptr_t)&lh);
26330Sstevel@tonic-gate 
26340Sstevel@tonic-gate 	cpu = mdb_alloc(sizeof (uintptr_t) * NCPU, UM_SLEEP | UM_GC);
26350Sstevel@tonic-gate 
26360Sstevel@tonic-gate 	if (mdb_lookup_by_name("cpu", &sym) == -1) {
26370Sstevel@tonic-gate 		mdb_warn("couldn't find 'cpu' array");
26380Sstevel@tonic-gate 		return (DCMD_ERR);
26390Sstevel@tonic-gate 	}
26400Sstevel@tonic-gate 
26410Sstevel@tonic-gate 	if (sym.st_size != NCPU * sizeof (uintptr_t)) {
26420Sstevel@tonic-gate 		mdb_warn("expected 'cpu' to be of size %d; found %d\n",
26430Sstevel@tonic-gate 		    NCPU * sizeof (uintptr_t), sym.st_size);
26440Sstevel@tonic-gate 		return (DCMD_ERR);
26450Sstevel@tonic-gate 	}
26460Sstevel@tonic-gate 
26470Sstevel@tonic-gate 	if (mdb_vread(cpu, sym.st_size, (uintptr_t)sym.st_value) == -1) {
26480Sstevel@tonic-gate 		mdb_warn("failed to read cpu array at %p", sym.st_value);
26490Sstevel@tonic-gate 		return (DCMD_ERR);
26500Sstevel@tonic-gate 	}
26510Sstevel@tonic-gate 
26520Sstevel@tonic-gate 	kmc = mdb_zalloc(sizeof (kmem_log_cpu_t) * NCPU, UM_SLEEP | UM_GC);
26530Sstevel@tonic-gate 	kmd.kmd_addr = NULL;
26540Sstevel@tonic-gate 	kmd.kmd_cpu = kmc;
26550Sstevel@tonic-gate 
26560Sstevel@tonic-gate 	for (i = 0; i < NCPU; i++) {
26570Sstevel@tonic-gate 
26580Sstevel@tonic-gate 		if (cpu[i] == NULL)
26590Sstevel@tonic-gate 			continue;
26600Sstevel@tonic-gate 
26610Sstevel@tonic-gate 		if (mdb_vread(&clh, sizeof (clh), clhp) == -1) {
26620Sstevel@tonic-gate 			mdb_warn("cannot read cpu %d's log header at %p",
26630Sstevel@tonic-gate 			    i, clhp);
26640Sstevel@tonic-gate 			return (DCMD_ERR);
26650Sstevel@tonic-gate 		}
26660Sstevel@tonic-gate 
26670Sstevel@tonic-gate 		kmc[i].kmc_low = clh.clh_chunk * lh.lh_chunksize +
26680Sstevel@tonic-gate 		    (uintptr_t)lh.lh_base;
26690Sstevel@tonic-gate 		kmc[i].kmc_high = (uintptr_t)clh.clh_current;
26700Sstevel@tonic-gate 
26710Sstevel@tonic-gate 		clhp += sizeof (kmem_cpu_log_header_t);
26720Sstevel@tonic-gate 	}
26730Sstevel@tonic-gate 
26740Sstevel@tonic-gate 	mdb_printf("%3s %-?s %-?s %16s %-?s\n", "CPU", "ADDR", "BUFADDR",
26750Sstevel@tonic-gate 	    "TIMESTAMP", "THREAD");
26760Sstevel@tonic-gate 
26770Sstevel@tonic-gate 	/*
26780Sstevel@tonic-gate 	 * If we have been passed an address, print out only log entries
26790Sstevel@tonic-gate 	 * corresponding to that address.  If opt_b is specified, then interpret
26800Sstevel@tonic-gate 	 * the address as a bufctl.
26810Sstevel@tonic-gate 	 */
26820Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC) {
26830Sstevel@tonic-gate 		kmem_bufctl_audit_t b;
26840Sstevel@tonic-gate 
26850Sstevel@tonic-gate 		if (opt_b) {
26860Sstevel@tonic-gate 			kmd.kmd_addr = addr;
26870Sstevel@tonic-gate 		} else {
26880Sstevel@tonic-gate 			if (mdb_vread(&b,
26890Sstevel@tonic-gate 			    sizeof (kmem_bufctl_audit_t), addr) == -1) {
26900Sstevel@tonic-gate 				mdb_warn("failed to read bufctl at %p", addr);
26910Sstevel@tonic-gate 				return (DCMD_ERR);
26920Sstevel@tonic-gate 			}
26930Sstevel@tonic-gate 
26940Sstevel@tonic-gate 			(void) kmem_log_walk(addr, &b, &kmd);
26950Sstevel@tonic-gate 
26960Sstevel@tonic-gate 			return (DCMD_OK);
26970Sstevel@tonic-gate 		}
26980Sstevel@tonic-gate 	}
26990Sstevel@tonic-gate 
27000Sstevel@tonic-gate 	if (mdb_walk("kmem_log", (mdb_walk_cb_t)kmem_log_walk, &kmd) == -1) {
27010Sstevel@tonic-gate 		mdb_warn("can't find kmem log walker");
27020Sstevel@tonic-gate 		return (DCMD_ERR);
27030Sstevel@tonic-gate 	}
27040Sstevel@tonic-gate 
27050Sstevel@tonic-gate 	return (DCMD_OK);
27060Sstevel@tonic-gate }
27070Sstevel@tonic-gate 
27080Sstevel@tonic-gate typedef struct bufctl_history_cb {
27090Sstevel@tonic-gate 	int		bhc_flags;
27100Sstevel@tonic-gate 	int		bhc_argc;
27110Sstevel@tonic-gate 	const mdb_arg_t	*bhc_argv;
27120Sstevel@tonic-gate 	int		bhc_ret;
27130Sstevel@tonic-gate } bufctl_history_cb_t;
27140Sstevel@tonic-gate 
27150Sstevel@tonic-gate /*ARGSUSED*/
27160Sstevel@tonic-gate static int
27170Sstevel@tonic-gate bufctl_history_callback(uintptr_t addr, const void *ign, void *arg)
27180Sstevel@tonic-gate {
27190Sstevel@tonic-gate 	bufctl_history_cb_t *bhc = arg;
27200Sstevel@tonic-gate 
27210Sstevel@tonic-gate 	bhc->bhc_ret =
27220Sstevel@tonic-gate 	    bufctl(addr, bhc->bhc_flags, bhc->bhc_argc, bhc->bhc_argv);
27230Sstevel@tonic-gate 
27240Sstevel@tonic-gate 	bhc->bhc_flags &= ~DCMD_LOOPFIRST;
27250Sstevel@tonic-gate 
27260Sstevel@tonic-gate 	return ((bhc->bhc_ret == DCMD_OK)? WALK_NEXT : WALK_DONE);
27270Sstevel@tonic-gate }
27280Sstevel@tonic-gate 
27290Sstevel@tonic-gate void
27300Sstevel@tonic-gate bufctl_help(void)
27310Sstevel@tonic-gate {
27320Sstevel@tonic-gate 	mdb_printf("%s\n",
27330Sstevel@tonic-gate "Display the contents of kmem_bufctl_audit_ts, with optional filtering.\n");
27340Sstevel@tonic-gate 	mdb_dec_indent(2);
27350Sstevel@tonic-gate 	mdb_printf("%<b>OPTIONS%</b>\n");
27360Sstevel@tonic-gate 	mdb_inc_indent(2);
27370Sstevel@tonic-gate 	mdb_printf("%s",
27380Sstevel@tonic-gate "  -v    Display the full content of the bufctl, including its stack trace\n"
27390Sstevel@tonic-gate "  -h    retrieve the bufctl's transaction history, if available\n"
27400Sstevel@tonic-gate "  -a addr\n"
27410Sstevel@tonic-gate "        filter out bufctls not involving the buffer at addr\n"
27420Sstevel@tonic-gate "  -c caller\n"
27430Sstevel@tonic-gate "        filter out bufctls without the function/PC in their stack trace\n"
27440Sstevel@tonic-gate "  -e earliest\n"
27450Sstevel@tonic-gate "        filter out bufctls timestamped before earliest\n"
27460Sstevel@tonic-gate "  -l latest\n"
27470Sstevel@tonic-gate "        filter out bufctls timestamped after latest\n"
27480Sstevel@tonic-gate "  -t thread\n"
27490Sstevel@tonic-gate "        filter out bufctls not involving thread\n");
27500Sstevel@tonic-gate }
27510Sstevel@tonic-gate 
27520Sstevel@tonic-gate int
27530Sstevel@tonic-gate bufctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
27540Sstevel@tonic-gate {
27550Sstevel@tonic-gate 	kmem_bufctl_audit_t bc;
27560Sstevel@tonic-gate 	uint_t verbose = FALSE;
27570Sstevel@tonic-gate 	uint_t history = FALSE;
27580Sstevel@tonic-gate 	uint_t in_history = FALSE;
27590Sstevel@tonic-gate 	uintptr_t caller = NULL, thread = NULL;
27600Sstevel@tonic-gate 	uintptr_t laddr, haddr, baddr = NULL;
27610Sstevel@tonic-gate 	hrtime_t earliest = 0, latest = 0;
27620Sstevel@tonic-gate 	int i, depth;
27630Sstevel@tonic-gate 	char c[MDB_SYM_NAMLEN];
27640Sstevel@tonic-gate 	GElf_Sym sym;
27650Sstevel@tonic-gate 
27660Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
27670Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
27680Sstevel@tonic-gate 	    'h', MDB_OPT_SETBITS, TRUE, &history,
27690Sstevel@tonic-gate 	    'H', MDB_OPT_SETBITS, TRUE, &in_history,		/* internal */
27700Sstevel@tonic-gate 	    'c', MDB_OPT_UINTPTR, &caller,
27710Sstevel@tonic-gate 	    't', MDB_OPT_UINTPTR, &thread,
27720Sstevel@tonic-gate 	    'e', MDB_OPT_UINT64, &earliest,
27730Sstevel@tonic-gate 	    'l', MDB_OPT_UINT64, &latest,
27740Sstevel@tonic-gate 	    'a', MDB_OPT_UINTPTR, &baddr, NULL) != argc)
27750Sstevel@tonic-gate 		return (DCMD_USAGE);
27760Sstevel@tonic-gate 
27770Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
27780Sstevel@tonic-gate 		return (DCMD_USAGE);
27790Sstevel@tonic-gate 
27800Sstevel@tonic-gate 	if (in_history && !history)
27810Sstevel@tonic-gate 		return (DCMD_USAGE);
27820Sstevel@tonic-gate 
27830Sstevel@tonic-gate 	if (history && !in_history) {
27840Sstevel@tonic-gate 		mdb_arg_t *nargv = mdb_zalloc(sizeof (*nargv) * (argc + 1),
27850Sstevel@tonic-gate 		    UM_SLEEP | UM_GC);
27860Sstevel@tonic-gate 		bufctl_history_cb_t bhc;
27870Sstevel@tonic-gate 
27880Sstevel@tonic-gate 		nargv[0].a_type = MDB_TYPE_STRING;
27890Sstevel@tonic-gate 		nargv[0].a_un.a_str = "-H";		/* prevent recursion */
27900Sstevel@tonic-gate 
27910Sstevel@tonic-gate 		for (i = 0; i < argc; i++)
27920Sstevel@tonic-gate 			nargv[i + 1] = argv[i];
27930Sstevel@tonic-gate 
27940Sstevel@tonic-gate 		/*
27950Sstevel@tonic-gate 		 * When in history mode, we treat each element as if it
27960Sstevel@tonic-gate 		 * were in a seperate loop, so that the headers group
27970Sstevel@tonic-gate 		 * bufctls with similar histories.
27980Sstevel@tonic-gate 		 */
27990Sstevel@tonic-gate 		bhc.bhc_flags = flags | DCMD_LOOP | DCMD_LOOPFIRST;
28000Sstevel@tonic-gate 		bhc.bhc_argc = argc + 1;
28010Sstevel@tonic-gate 		bhc.bhc_argv = nargv;
28020Sstevel@tonic-gate 		bhc.bhc_ret = DCMD_OK;
28030Sstevel@tonic-gate 
28040Sstevel@tonic-gate 		if (mdb_pwalk("bufctl_history", bufctl_history_callback, &bhc,
28050Sstevel@tonic-gate 		    addr) == -1) {
28060Sstevel@tonic-gate 			mdb_warn("unable to walk bufctl_history");
28070Sstevel@tonic-gate 			return (DCMD_ERR);
28080Sstevel@tonic-gate 		}
28090Sstevel@tonic-gate 
28100Sstevel@tonic-gate 		if (bhc.bhc_ret == DCMD_OK && !(flags & DCMD_PIPE_OUT))
28110Sstevel@tonic-gate 			mdb_printf("\n");
28120Sstevel@tonic-gate 
28130Sstevel@tonic-gate 		return (bhc.bhc_ret);
28140Sstevel@tonic-gate 	}
28150Sstevel@tonic-gate 
28160Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
28170Sstevel@tonic-gate 		if (verbose) {
28180Sstevel@tonic-gate 			mdb_printf("%16s %16s %16s %16s\n"
28190Sstevel@tonic-gate 			    "%<u>%16s %16s %16s %16s%</u>\n",
28200Sstevel@tonic-gate 			    "ADDR", "BUFADDR", "TIMESTAMP", "THREAD",
28210Sstevel@tonic-gate 			    "", "CACHE", "LASTLOG", "CONTENTS");
28220Sstevel@tonic-gate 		} else {
28230Sstevel@tonic-gate 			mdb_printf("%<u>%-?s %-?s %-12s %-?s %s%</u>\n",
28240Sstevel@tonic-gate 			    "ADDR", "BUFADDR", "TIMESTAMP", "THREAD", "CALLER");
28250Sstevel@tonic-gate 		}
28260Sstevel@tonic-gate 	}
28270Sstevel@tonic-gate 
28280Sstevel@tonic-gate 	if (mdb_vread(&bc, sizeof (bc), addr) == -1) {
28290Sstevel@tonic-gate 		mdb_warn("couldn't read bufctl at %p", addr);
28300Sstevel@tonic-gate 		return (DCMD_ERR);
28310Sstevel@tonic-gate 	}
28320Sstevel@tonic-gate 
28330Sstevel@tonic-gate 	/*
28340Sstevel@tonic-gate 	 * Guard against bogus bc_depth in case the bufctl is corrupt or
28350Sstevel@tonic-gate 	 * the address does not really refer to a bufctl.
28360Sstevel@tonic-gate 	 */
28370Sstevel@tonic-gate 	depth = MIN(bc.bc_depth, KMEM_STACK_DEPTH);
28380Sstevel@tonic-gate 
28390Sstevel@tonic-gate 	if (caller != NULL) {
28400Sstevel@tonic-gate 		laddr = caller;
28410Sstevel@tonic-gate 		haddr = caller + sizeof (caller);
28420Sstevel@tonic-gate 
28430Sstevel@tonic-gate 		if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c, sizeof (c),
28440Sstevel@tonic-gate 		    &sym) != -1 && caller == (uintptr_t)sym.st_value) {
28450Sstevel@tonic-gate 			/*
28460Sstevel@tonic-gate 			 * We were provided an exact symbol value; any
28470Sstevel@tonic-gate 			 * address in the function is valid.
28480Sstevel@tonic-gate 			 */
28490Sstevel@tonic-gate 			laddr = (uintptr_t)sym.st_value;
28500Sstevel@tonic-gate 			haddr = (uintptr_t)sym.st_value + sym.st_size;
28510Sstevel@tonic-gate 		}
28520Sstevel@tonic-gate 
28530Sstevel@tonic-gate 		for (i = 0; i < depth; i++)
28540Sstevel@tonic-gate 			if (bc.bc_stack[i] >= laddr && bc.bc_stack[i] < haddr)
28550Sstevel@tonic-gate 				break;
28560Sstevel@tonic-gate 
28570Sstevel@tonic-gate 		if (i == depth)
28580Sstevel@tonic-gate 			return (DCMD_OK);
28590Sstevel@tonic-gate 	}
28600Sstevel@tonic-gate 
28610Sstevel@tonic-gate 	if (thread != NULL && (uintptr_t)bc.bc_thread != thread)
28620Sstevel@tonic-gate 		return (DCMD_OK);
28630Sstevel@tonic-gate 
28640Sstevel@tonic-gate 	if (earliest != 0 && bc.bc_timestamp < earliest)
28650Sstevel@tonic-gate 		return (DCMD_OK);
28660Sstevel@tonic-gate 
28670Sstevel@tonic-gate 	if (latest != 0 && bc.bc_timestamp > latest)
28680Sstevel@tonic-gate 		return (DCMD_OK);
28690Sstevel@tonic-gate 
28700Sstevel@tonic-gate 	if (baddr != 0 && (uintptr_t)bc.bc_addr != baddr)
28710Sstevel@tonic-gate 		return (DCMD_OK);
28720Sstevel@tonic-gate 
28730Sstevel@tonic-gate 	if (flags & DCMD_PIPE_OUT) {
28740Sstevel@tonic-gate 		mdb_printf("%#lr\n", addr);
28750Sstevel@tonic-gate 		return (DCMD_OK);
28760Sstevel@tonic-gate 	}
28770Sstevel@tonic-gate 
28780Sstevel@tonic-gate 	if (verbose) {
28790Sstevel@tonic-gate 		mdb_printf(
28800Sstevel@tonic-gate 		    "%<b>%16p%</b> %16p %16llx %16p\n"
28810Sstevel@tonic-gate 		    "%16s %16p %16p %16p\n",
28820Sstevel@tonic-gate 		    addr, bc.bc_addr, bc.bc_timestamp, bc.bc_thread,
28830Sstevel@tonic-gate 		    "", bc.bc_cache, bc.bc_lastlog, bc.bc_contents);
28840Sstevel@tonic-gate 
28850Sstevel@tonic-gate 		mdb_inc_indent(17);
28860Sstevel@tonic-gate 		for (i = 0; i < depth; i++)
28870Sstevel@tonic-gate 			mdb_printf("%a\n", bc.bc_stack[i]);
28880Sstevel@tonic-gate 		mdb_dec_indent(17);
28890Sstevel@tonic-gate 		mdb_printf("\n");
28900Sstevel@tonic-gate 	} else {
28910Sstevel@tonic-gate 		mdb_printf("%0?p %0?p %12llx %0?p", addr, bc.bc_addr,
28920Sstevel@tonic-gate 		    bc.bc_timestamp, bc.bc_thread);
28930Sstevel@tonic-gate 
28940Sstevel@tonic-gate 		for (i = 0; i < depth; i++) {
28950Sstevel@tonic-gate 			if (mdb_lookup_by_addr(bc.bc_stack[i],
28960Sstevel@tonic-gate 			    MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1)
28970Sstevel@tonic-gate 				continue;
28980Sstevel@tonic-gate 			if (strncmp(c, "kmem_", 5) == 0)
28990Sstevel@tonic-gate 				continue;
29000Sstevel@tonic-gate 			mdb_printf(" %a\n", bc.bc_stack[i]);
29010Sstevel@tonic-gate 			break;
29020Sstevel@tonic-gate 		}
29030Sstevel@tonic-gate 
29040Sstevel@tonic-gate 		if (i >= depth)
29050Sstevel@tonic-gate 			mdb_printf("\n");
29060Sstevel@tonic-gate 	}
29070Sstevel@tonic-gate 
29080Sstevel@tonic-gate 	return (DCMD_OK);
29090Sstevel@tonic-gate }
29100Sstevel@tonic-gate 
29110Sstevel@tonic-gate typedef struct kmem_verify {
29120Sstevel@tonic-gate 	uint64_t *kmv_buf;		/* buffer to read cache contents into */
29130Sstevel@tonic-gate 	size_t kmv_size;		/* number of bytes in kmv_buf */
29140Sstevel@tonic-gate 	int kmv_corruption;		/* > 0 if corruption found. */
29150Sstevel@tonic-gate 	int kmv_besilent;		/* report actual corruption sites */
29160Sstevel@tonic-gate 	struct kmem_cache kmv_cache;	/* the cache we're operating on */
29170Sstevel@tonic-gate } kmem_verify_t;
29180Sstevel@tonic-gate 
29190Sstevel@tonic-gate /*
29200Sstevel@tonic-gate  * verify_pattern()
29210Sstevel@tonic-gate  * 	verify that buf is filled with the pattern pat.
29220Sstevel@tonic-gate  */
29230Sstevel@tonic-gate static int64_t
29240Sstevel@tonic-gate verify_pattern(uint64_t *buf_arg, size_t size, uint64_t pat)
29250Sstevel@tonic-gate {
29260Sstevel@tonic-gate 	/*LINTED*/
29270Sstevel@tonic-gate 	uint64_t *bufend = (uint64_t *)((char *)buf_arg + size);
29280Sstevel@tonic-gate 	uint64_t *buf;
29290Sstevel@tonic-gate 
29300Sstevel@tonic-gate 	for (buf = buf_arg; buf < bufend; buf++)
29310Sstevel@tonic-gate 		if (*buf != pat)
29320Sstevel@tonic-gate 			return ((uintptr_t)buf - (uintptr_t)buf_arg);
29330Sstevel@tonic-gate 	return (-1);
29340Sstevel@tonic-gate }
29350Sstevel@tonic-gate 
29360Sstevel@tonic-gate /*
29370Sstevel@tonic-gate  * verify_buftag()
29380Sstevel@tonic-gate  *	verify that btp->bt_bxstat == (bcp ^ pat)
29390Sstevel@tonic-gate  */
29400Sstevel@tonic-gate static int
29410Sstevel@tonic-gate verify_buftag(kmem_buftag_t *btp, uintptr_t pat)
29420Sstevel@tonic-gate {
29430Sstevel@tonic-gate 	return (btp->bt_bxstat == ((intptr_t)btp->bt_bufctl ^ pat) ? 0 : -1);
29440Sstevel@tonic-gate }
29450Sstevel@tonic-gate 
29460Sstevel@tonic-gate /*
29470Sstevel@tonic-gate  * verify_free()
29480Sstevel@tonic-gate  * 	verify the integrity of a free block of memory by checking
29490Sstevel@tonic-gate  * 	that it is filled with 0xdeadbeef and that its buftag is sane.
29500Sstevel@tonic-gate  */
29510Sstevel@tonic-gate /*ARGSUSED1*/
29520Sstevel@tonic-gate static int
29530Sstevel@tonic-gate verify_free(uintptr_t addr, const void *data, void *private)
29540Sstevel@tonic-gate {
29550Sstevel@tonic-gate 	kmem_verify_t *kmv = (kmem_verify_t *)private;
29560Sstevel@tonic-gate 	uint64_t *buf = kmv->kmv_buf;	/* buf to validate */
29570Sstevel@tonic-gate 	int64_t corrupt;		/* corruption offset */
29580Sstevel@tonic-gate 	kmem_buftag_t *buftagp;		/* ptr to buftag */
29590Sstevel@tonic-gate 	kmem_cache_t *cp = &kmv->kmv_cache;
29600Sstevel@tonic-gate 	int besilent = kmv->kmv_besilent;
29610Sstevel@tonic-gate 
29620Sstevel@tonic-gate 	/*LINTED*/
29630Sstevel@tonic-gate 	buftagp = KMEM_BUFTAG(cp, buf);
29640Sstevel@tonic-gate 
29650Sstevel@tonic-gate 	/*
29660Sstevel@tonic-gate 	 * Read the buffer to check.
29670Sstevel@tonic-gate 	 */
29680Sstevel@tonic-gate 	if (mdb_vread(buf, kmv->kmv_size, addr) == -1) {
29690Sstevel@tonic-gate 		if (!besilent)
29700Sstevel@tonic-gate 			mdb_warn("couldn't read %p", addr);
29710Sstevel@tonic-gate 		return (WALK_NEXT);
29720Sstevel@tonic-gate 	}
29730Sstevel@tonic-gate 
29740Sstevel@tonic-gate 	if ((corrupt = verify_pattern(buf, cp->cache_verify,
29750Sstevel@tonic-gate 	    KMEM_FREE_PATTERN)) >= 0) {
29760Sstevel@tonic-gate 		if (!besilent)
29770Sstevel@tonic-gate 			mdb_printf("buffer %p (free) seems corrupted, at %p\n",
29780Sstevel@tonic-gate 			    addr, (uintptr_t)addr + corrupt);
29790Sstevel@tonic-gate 		goto corrupt;
29800Sstevel@tonic-gate 	}
29810Sstevel@tonic-gate 	/*
29820Sstevel@tonic-gate 	 * When KMF_LITE is set, buftagp->bt_redzone is used to hold
29830Sstevel@tonic-gate 	 * the first bytes of the buffer, hence we cannot check for red
29840Sstevel@tonic-gate 	 * zone corruption.
29850Sstevel@tonic-gate 	 */
29860Sstevel@tonic-gate 	if ((cp->cache_flags & (KMF_HASH | KMF_LITE)) == KMF_HASH &&
29870Sstevel@tonic-gate 	    buftagp->bt_redzone != KMEM_REDZONE_PATTERN) {
29880Sstevel@tonic-gate 		if (!besilent)
29890Sstevel@tonic-gate 			mdb_printf("buffer %p (free) seems to "
29900Sstevel@tonic-gate 			    "have a corrupt redzone pattern\n", addr);
29910Sstevel@tonic-gate 		goto corrupt;
29920Sstevel@tonic-gate 	}
29930Sstevel@tonic-gate 
29940Sstevel@tonic-gate 	/*
29950Sstevel@tonic-gate 	 * confirm bufctl pointer integrity.
29960Sstevel@tonic-gate 	 */
29970Sstevel@tonic-gate 	if (verify_buftag(buftagp, KMEM_BUFTAG_FREE) == -1) {
29980Sstevel@tonic-gate 		if (!besilent)
29990Sstevel@tonic-gate 			mdb_printf("buffer %p (free) has a corrupt "
30000Sstevel@tonic-gate 			    "buftag\n", addr);
30010Sstevel@tonic-gate 		goto corrupt;
30020Sstevel@tonic-gate 	}
30030Sstevel@tonic-gate 
30040Sstevel@tonic-gate 	return (WALK_NEXT);
30050Sstevel@tonic-gate corrupt:
30060Sstevel@tonic-gate 	kmv->kmv_corruption++;
30070Sstevel@tonic-gate 	return (WALK_NEXT);
30080Sstevel@tonic-gate }
30090Sstevel@tonic-gate 
30100Sstevel@tonic-gate /*
30110Sstevel@tonic-gate  * verify_alloc()
30120Sstevel@tonic-gate  * 	Verify that the buftag of an allocated buffer makes sense with respect
30130Sstevel@tonic-gate  * 	to the buffer.
30140Sstevel@tonic-gate  */
30150Sstevel@tonic-gate /*ARGSUSED1*/
30160Sstevel@tonic-gate static int
30170Sstevel@tonic-gate verify_alloc(uintptr_t addr, const void *data, void *private)
30180Sstevel@tonic-gate {
30190Sstevel@tonic-gate 	kmem_verify_t *kmv = (kmem_verify_t *)private;
30200Sstevel@tonic-gate 	kmem_cache_t *cp = &kmv->kmv_cache;
30210Sstevel@tonic-gate 	uint64_t *buf = kmv->kmv_buf;	/* buf to validate */
30220Sstevel@tonic-gate 	/*LINTED*/
30230Sstevel@tonic-gate 	kmem_buftag_t *buftagp = KMEM_BUFTAG(cp, buf);
30240Sstevel@tonic-gate 	uint32_t *ip = (uint32_t *)buftagp;
30250Sstevel@tonic-gate 	uint8_t *bp = (uint8_t *)buf;
30260Sstevel@tonic-gate 	int looks_ok = 0, size_ok = 1;	/* flags for finding corruption */
30270Sstevel@tonic-gate 	int besilent = kmv->kmv_besilent;
30280Sstevel@tonic-gate 
30290Sstevel@tonic-gate 	/*
30300Sstevel@tonic-gate 	 * Read the buffer to check.
30310Sstevel@tonic-gate 	 */
30320Sstevel@tonic-gate 	if (mdb_vread(buf, kmv->kmv_size, addr) == -1) {
30330Sstevel@tonic-gate 		if (!besilent)
30340Sstevel@tonic-gate 			mdb_warn("couldn't read %p", addr);
30350Sstevel@tonic-gate 		return (WALK_NEXT);
30360Sstevel@tonic-gate 	}
30370Sstevel@tonic-gate 
30380Sstevel@tonic-gate 	/*
30390Sstevel@tonic-gate 	 * There are two cases to handle:
30400Sstevel@tonic-gate 	 * 1. If the buf was alloc'd using kmem_cache_alloc, it will have
30410Sstevel@tonic-gate 	 *    0xfeedfacefeedface at the end of it
30420Sstevel@tonic-gate 	 * 2. If the buf was alloc'd using kmem_alloc, it will have
30430Sstevel@tonic-gate 	 *    0xbb just past the end of the region in use.  At the buftag,
30440Sstevel@tonic-gate 	 *    it will have 0xfeedface (or, if the whole buffer is in use,
30450Sstevel@tonic-gate 	 *    0xfeedface & bb000000 or 0xfeedfacf & 000000bb depending on
30460Sstevel@tonic-gate 	 *    endianness), followed by 32 bits containing the offset of the
30470Sstevel@tonic-gate 	 *    0xbb byte in the buffer.
30480Sstevel@tonic-gate 	 *
30490Sstevel@tonic-gate 	 * Finally, the two 32-bit words that comprise the second half of the
30500Sstevel@tonic-gate 	 * buftag should xor to KMEM_BUFTAG_ALLOC
30510Sstevel@tonic-gate 	 */
30520Sstevel@tonic-gate 
30530Sstevel@tonic-gate 	if (buftagp->bt_redzone == KMEM_REDZONE_PATTERN)
30540Sstevel@tonic-gate 		looks_ok = 1;
30550Sstevel@tonic-gate 	else if (!KMEM_SIZE_VALID(ip[1]))
30560Sstevel@tonic-gate 		size_ok = 0;
30570Sstevel@tonic-gate 	else if (bp[KMEM_SIZE_DECODE(ip[1])] == KMEM_REDZONE_BYTE)
30580Sstevel@tonic-gate 		looks_ok = 1;
30590Sstevel@tonic-gate 	else
30600Sstevel@tonic-gate 		size_ok = 0;
30610Sstevel@tonic-gate 
30620Sstevel@tonic-gate 	if (!size_ok) {
30630Sstevel@tonic-gate 		if (!besilent)
30640Sstevel@tonic-gate 			mdb_printf("buffer %p (allocated) has a corrupt "
30650Sstevel@tonic-gate 			    "redzone size encoding\n", addr);
30660Sstevel@tonic-gate 		goto corrupt;
30670Sstevel@tonic-gate 	}
30680Sstevel@tonic-gate 
30690Sstevel@tonic-gate 	if (!looks_ok) {
30700Sstevel@tonic-gate 		if (!besilent)
30710Sstevel@tonic-gate 			mdb_printf("buffer %p (allocated) has a corrupt "
30720Sstevel@tonic-gate 			    "redzone signature\n", addr);
30730Sstevel@tonic-gate 		goto corrupt;
30740Sstevel@tonic-gate 	}
30750Sstevel@tonic-gate 
30760Sstevel@tonic-gate 	if (verify_buftag(buftagp, KMEM_BUFTAG_ALLOC) == -1) {
30770Sstevel@tonic-gate 		if (!besilent)
30780Sstevel@tonic-gate 			mdb_printf("buffer %p (allocated) has a "
30790Sstevel@tonic-gate 			    "corrupt buftag\n", addr);
30800Sstevel@tonic-gate 		goto corrupt;
30810Sstevel@tonic-gate 	}
30820Sstevel@tonic-gate 
30830Sstevel@tonic-gate 	return (WALK_NEXT);
30840Sstevel@tonic-gate corrupt:
30850Sstevel@tonic-gate 	kmv->kmv_corruption++;
30860Sstevel@tonic-gate 	return (WALK_NEXT);
30870Sstevel@tonic-gate }
30880Sstevel@tonic-gate 
30890Sstevel@tonic-gate /*ARGSUSED2*/
30900Sstevel@tonic-gate int
30910Sstevel@tonic-gate kmem_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
30920Sstevel@tonic-gate {
30930Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC) {
30940Sstevel@tonic-gate 		int check_alloc = 0, check_free = 0;
30950Sstevel@tonic-gate 		kmem_verify_t kmv;
30960Sstevel@tonic-gate 
30970Sstevel@tonic-gate 		if (mdb_vread(&kmv.kmv_cache, sizeof (kmv.kmv_cache),
30980Sstevel@tonic-gate 		    addr) == -1) {
30990Sstevel@tonic-gate 			mdb_warn("couldn't read kmem_cache %p", addr);
31000Sstevel@tonic-gate 			return (DCMD_ERR);
31010Sstevel@tonic-gate 		}
31020Sstevel@tonic-gate 
31030Sstevel@tonic-gate 		kmv.kmv_size = kmv.kmv_cache.cache_buftag +
31040Sstevel@tonic-gate 		    sizeof (kmem_buftag_t);
31050Sstevel@tonic-gate 		kmv.kmv_buf = mdb_alloc(kmv.kmv_size, UM_SLEEP | UM_GC);
31060Sstevel@tonic-gate 		kmv.kmv_corruption = 0;
31070Sstevel@tonic-gate 
31080Sstevel@tonic-gate 		if ((kmv.kmv_cache.cache_flags & KMF_REDZONE)) {
31090Sstevel@tonic-gate 			check_alloc = 1;
31100Sstevel@tonic-gate 			if (kmv.kmv_cache.cache_flags & KMF_DEADBEEF)
31110Sstevel@tonic-gate 				check_free = 1;
31120Sstevel@tonic-gate 		} else {
31130Sstevel@tonic-gate 			if (!(flags & DCMD_LOOP)) {
31140Sstevel@tonic-gate 				mdb_warn("cache %p (%s) does not have "
31150Sstevel@tonic-gate 				    "redzone checking enabled\n", addr,
31160Sstevel@tonic-gate 				    kmv.kmv_cache.cache_name);
31170Sstevel@tonic-gate 			}
31180Sstevel@tonic-gate 			return (DCMD_ERR);
31190Sstevel@tonic-gate 		}
31200Sstevel@tonic-gate 
31210Sstevel@tonic-gate 		if (flags & DCMD_LOOP) {
31220Sstevel@tonic-gate 			/*
31230Sstevel@tonic-gate 			 * table mode, don't print out every corrupt buffer
31240Sstevel@tonic-gate 			 */
31250Sstevel@tonic-gate 			kmv.kmv_besilent = 1;
31260Sstevel@tonic-gate 		} else {
31270Sstevel@tonic-gate 			mdb_printf("Summary for cache '%s'\n",
31280Sstevel@tonic-gate 			    kmv.kmv_cache.cache_name);
31290Sstevel@tonic-gate 			mdb_inc_indent(2);
31300Sstevel@tonic-gate 			kmv.kmv_besilent = 0;
31310Sstevel@tonic-gate 		}
31320Sstevel@tonic-gate 
31330Sstevel@tonic-gate 		if (check_alloc)
31340Sstevel@tonic-gate 			(void) mdb_pwalk("kmem", verify_alloc, &kmv, addr);
31350Sstevel@tonic-gate 		if (check_free)
31360Sstevel@tonic-gate 			(void) mdb_pwalk("freemem", verify_free, &kmv, addr);
31370Sstevel@tonic-gate 
31380Sstevel@tonic-gate 		if (flags & DCMD_LOOP) {
31390Sstevel@tonic-gate 			if (kmv.kmv_corruption == 0) {
31400Sstevel@tonic-gate 				mdb_printf("%-*s %?p clean\n",
31410Sstevel@tonic-gate 				    KMEM_CACHE_NAMELEN,
31420Sstevel@tonic-gate 				    kmv.kmv_cache.cache_name, addr);
31430Sstevel@tonic-gate 			} else {
31440Sstevel@tonic-gate 				char *s = "";	/* optional s in "buffer[s]" */
31450Sstevel@tonic-gate 				if (kmv.kmv_corruption > 1)
31460Sstevel@tonic-gate 					s = "s";
31470Sstevel@tonic-gate 
31480Sstevel@tonic-gate 				mdb_printf("%-*s %?p %d corrupt buffer%s\n",
31490Sstevel@tonic-gate 				    KMEM_CACHE_NAMELEN,
31500Sstevel@tonic-gate 				    kmv.kmv_cache.cache_name, addr,
31510Sstevel@tonic-gate 				    kmv.kmv_corruption, s);
31520Sstevel@tonic-gate 			}
31530Sstevel@tonic-gate 		} else {
31540Sstevel@tonic-gate 			/*
31550Sstevel@tonic-gate 			 * This is the more verbose mode, when the user has
31560Sstevel@tonic-gate 			 * type addr::kmem_verify.  If the cache was clean,
31570Sstevel@tonic-gate 			 * nothing will have yet been printed. So say something.
31580Sstevel@tonic-gate 			 */
31590Sstevel@tonic-gate 			if (kmv.kmv_corruption == 0)
31600Sstevel@tonic-gate 				mdb_printf("clean\n");
31610Sstevel@tonic-gate 
31620Sstevel@tonic-gate 			mdb_dec_indent(2);
31630Sstevel@tonic-gate 		}
31640Sstevel@tonic-gate 	} else {
31650Sstevel@tonic-gate 		/*
31660Sstevel@tonic-gate 		 * If the user didn't specify a cache to verify, we'll walk all
31670Sstevel@tonic-gate 		 * kmem_cache's, specifying ourself as a callback for each...
31680Sstevel@tonic-gate 		 * this is the equivalent of '::walk kmem_cache .::kmem_verify'
31690Sstevel@tonic-gate 		 */
31700Sstevel@tonic-gate 		mdb_printf("%<u>%-*s %-?s %-20s%</b>\n", KMEM_CACHE_NAMELEN,
31710Sstevel@tonic-gate 		    "Cache Name", "Addr", "Cache Integrity");
31720Sstevel@tonic-gate 		(void) (mdb_walk_dcmd("kmem_cache", "kmem_verify", 0, NULL));
31730Sstevel@tonic-gate 	}
31740Sstevel@tonic-gate 
31750Sstevel@tonic-gate 	return (DCMD_OK);
31760Sstevel@tonic-gate }
31770Sstevel@tonic-gate 
31780Sstevel@tonic-gate typedef struct vmem_node {
31790Sstevel@tonic-gate 	struct vmem_node *vn_next;
31800Sstevel@tonic-gate 	struct vmem_node *vn_parent;
31810Sstevel@tonic-gate 	struct vmem_node *vn_sibling;
31820Sstevel@tonic-gate 	struct vmem_node *vn_children;
31830Sstevel@tonic-gate 	uintptr_t vn_addr;
31840Sstevel@tonic-gate 	int vn_marked;
31850Sstevel@tonic-gate 	vmem_t vn_vmem;
31860Sstevel@tonic-gate } vmem_node_t;
31870Sstevel@tonic-gate 
31880Sstevel@tonic-gate typedef struct vmem_walk {
31890Sstevel@tonic-gate 	vmem_node_t *vw_root;
31900Sstevel@tonic-gate 	vmem_node_t *vw_current;
31910Sstevel@tonic-gate } vmem_walk_t;
31920Sstevel@tonic-gate 
31930Sstevel@tonic-gate int
31940Sstevel@tonic-gate vmem_walk_init(mdb_walk_state_t *wsp)
31950Sstevel@tonic-gate {
31960Sstevel@tonic-gate 	uintptr_t vaddr, paddr;
31970Sstevel@tonic-gate 	vmem_node_t *head = NULL, *root = NULL, *current = NULL, *parent, *vp;
31980Sstevel@tonic-gate 	vmem_walk_t *vw;
31990Sstevel@tonic-gate 
32000Sstevel@tonic-gate 	if (mdb_readvar(&vaddr, "vmem_list") == -1) {
32010Sstevel@tonic-gate 		mdb_warn("couldn't read 'vmem_list'");
32020Sstevel@tonic-gate 		return (WALK_ERR);
32030Sstevel@tonic-gate 	}
32040Sstevel@tonic-gate 
32050Sstevel@tonic-gate 	while (vaddr != NULL) {
32060Sstevel@tonic-gate 		vp = mdb_zalloc(sizeof (vmem_node_t), UM_SLEEP);
32070Sstevel@tonic-gate 		vp->vn_addr = vaddr;
32080Sstevel@tonic-gate 		vp->vn_next = head;
32090Sstevel@tonic-gate 		head = vp;
32100Sstevel@tonic-gate 
32110Sstevel@tonic-gate 		if (vaddr == wsp->walk_addr)
32120Sstevel@tonic-gate 			current = vp;
32130Sstevel@tonic-gate 
32140Sstevel@tonic-gate 		if (mdb_vread(&vp->vn_vmem, sizeof (vmem_t), vaddr) == -1) {
32150Sstevel@tonic-gate 			mdb_warn("couldn't read vmem_t at %p", vaddr);
32160Sstevel@tonic-gate 			goto err;
32170Sstevel@tonic-gate 		}
32180Sstevel@tonic-gate 
32190Sstevel@tonic-gate 		vaddr = (uintptr_t)vp->vn_vmem.vm_next;
32200Sstevel@tonic-gate 	}
32210Sstevel@tonic-gate 
32220Sstevel@tonic-gate 	for (vp = head; vp != NULL; vp = vp->vn_next) {
32230Sstevel@tonic-gate 
32240Sstevel@tonic-gate 		if ((paddr = (uintptr_t)vp->vn_vmem.vm_source) == NULL) {
32250Sstevel@tonic-gate 			vp->vn_sibling = root;
32260Sstevel@tonic-gate 			root = vp;
32270Sstevel@tonic-gate 			continue;
32280Sstevel@tonic-gate 		}
32290Sstevel@tonic-gate 
32300Sstevel@tonic-gate 		for (parent = head; parent != NULL; parent = parent->vn_next) {
32310Sstevel@tonic-gate 			if (parent->vn_addr != paddr)
32320Sstevel@tonic-gate 				continue;
32330Sstevel@tonic-gate 			vp->vn_sibling = parent->vn_children;
32340Sstevel@tonic-gate 			parent->vn_children = vp;
32350Sstevel@tonic-gate 			vp->vn_parent = parent;
32360Sstevel@tonic-gate 			break;
32370Sstevel@tonic-gate 		}
32380Sstevel@tonic-gate 
32390Sstevel@tonic-gate 		if (parent == NULL) {
32400Sstevel@tonic-gate 			mdb_warn("couldn't find %p's parent (%p)\n",
32410Sstevel@tonic-gate 			    vp->vn_addr, paddr);
32420Sstevel@tonic-gate 			goto err;
32430Sstevel@tonic-gate 		}
32440Sstevel@tonic-gate 	}
32450Sstevel@tonic-gate 
32460Sstevel@tonic-gate 	vw = mdb_zalloc(sizeof (vmem_walk_t), UM_SLEEP);
32470Sstevel@tonic-gate 	vw->vw_root = root;
32480Sstevel@tonic-gate 
32490Sstevel@tonic-gate 	if (current != NULL)
32500Sstevel@tonic-gate 		vw->vw_current = current;
32510Sstevel@tonic-gate 	else
32520Sstevel@tonic-gate 		vw->vw_current = root;
32530Sstevel@tonic-gate 
32540Sstevel@tonic-gate 	wsp->walk_data = vw;
32550Sstevel@tonic-gate 	return (WALK_NEXT);
32560Sstevel@tonic-gate err:
32570Sstevel@tonic-gate 	for (vp = head; head != NULL; vp = head) {
32580Sstevel@tonic-gate 		head = vp->vn_next;
32590Sstevel@tonic-gate 		mdb_free(vp, sizeof (vmem_node_t));
32600Sstevel@tonic-gate 	}
32610Sstevel@tonic-gate 
32620Sstevel@tonic-gate 	return (WALK_ERR);
32630Sstevel@tonic-gate }
32640Sstevel@tonic-gate 
32650Sstevel@tonic-gate int
32660Sstevel@tonic-gate vmem_walk_step(mdb_walk_state_t *wsp)
32670Sstevel@tonic-gate {
32680Sstevel@tonic-gate 	vmem_walk_t *vw = wsp->walk_data;
32690Sstevel@tonic-gate 	vmem_node_t *vp;
32700Sstevel@tonic-gate 	int rval;
32710Sstevel@tonic-gate 
32720Sstevel@tonic-gate 	if ((vp = vw->vw_current) == NULL)
32730Sstevel@tonic-gate 		return (WALK_DONE);
32740Sstevel@tonic-gate 
32750Sstevel@tonic-gate 	rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata);
32760Sstevel@tonic-gate 
32770Sstevel@tonic-gate 	if (vp->vn_children != NULL) {
32780Sstevel@tonic-gate 		vw->vw_current = vp->vn_children;
32790Sstevel@tonic-gate 		return (rval);
32800Sstevel@tonic-gate 	}
32810Sstevel@tonic-gate 
32820Sstevel@tonic-gate 	do {
32830Sstevel@tonic-gate 		vw->vw_current = vp->vn_sibling;
32840Sstevel@tonic-gate 		vp = vp->vn_parent;
32850Sstevel@tonic-gate 	} while (vw->vw_current == NULL && vp != NULL);
32860Sstevel@tonic-gate 
32870Sstevel@tonic-gate 	return (rval);
32880Sstevel@tonic-gate }
32890Sstevel@tonic-gate 
32900Sstevel@tonic-gate /*
32910Sstevel@tonic-gate  * The "vmem_postfix" walk walks the vmem arenas in post-fix order; all
32920Sstevel@tonic-gate  * children are visited before their parent.  We perform the postfix walk
32930Sstevel@tonic-gate  * iteratively (rather than recursively) to allow mdb to regain control
32940Sstevel@tonic-gate  * after each callback.
32950Sstevel@tonic-gate  */
32960Sstevel@tonic-gate int
32970Sstevel@tonic-gate vmem_postfix_walk_step(mdb_walk_state_t *wsp)
32980Sstevel@tonic-gate {
32990Sstevel@tonic-gate 	vmem_walk_t *vw = wsp->walk_data;
33000Sstevel@tonic-gate 	vmem_node_t *vp = vw->vw_current;
33010Sstevel@tonic-gate 	int rval;
33020Sstevel@tonic-gate 
33030Sstevel@tonic-gate 	/*
33040Sstevel@tonic-gate 	 * If this node is marked, then we know that we have already visited
33050Sstevel@tonic-gate 	 * all of its children.  If the node has any siblings, they need to
33060Sstevel@tonic-gate 	 * be visited next; otherwise, we need to visit the parent.  Note
33070Sstevel@tonic-gate 	 * that vp->vn_marked will only be zero on the first invocation of
33080Sstevel@tonic-gate 	 * the step function.
33090Sstevel@tonic-gate 	 */
33100Sstevel@tonic-gate 	if (vp->vn_marked) {
33110Sstevel@tonic-gate 		if (vp->vn_sibling != NULL)
33120Sstevel@tonic-gate 			vp = vp->vn_sibling;
33130Sstevel@tonic-gate 		else if (vp->vn_parent != NULL)
33140Sstevel@tonic-gate 			vp = vp->vn_parent;
33150Sstevel@tonic-gate 		else {
33160Sstevel@tonic-gate 			/*
33170Sstevel@tonic-gate 			 * We have neither a parent, nor a sibling, and we
33180Sstevel@tonic-gate 			 * have already been visited; we're done.
33190Sstevel@tonic-gate 			 */
33200Sstevel@tonic-gate 			return (WALK_DONE);
33210Sstevel@tonic-gate 		}
33220Sstevel@tonic-gate 	}
33230Sstevel@tonic-gate 
33240Sstevel@tonic-gate 	/*
33250Sstevel@tonic-gate 	 * Before we visit this node, visit its children.
33260Sstevel@tonic-gate 	 */
33270Sstevel@tonic-gate 	while (vp->vn_children != NULL && !vp->vn_children->vn_marked)
33280Sstevel@tonic-gate 		vp = vp->vn_children;
33290Sstevel@tonic-gate 
33300Sstevel@tonic-gate 	vp->vn_marked = 1;
33310Sstevel@tonic-gate 	vw->vw_current = vp;
33320Sstevel@tonic-gate 	rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata);
33330Sstevel@tonic-gate 
33340Sstevel@tonic-gate 	return (rval);
33350Sstevel@tonic-gate }
33360Sstevel@tonic-gate 
33370Sstevel@tonic-gate void
33380Sstevel@tonic-gate vmem_walk_fini(mdb_walk_state_t *wsp)
33390Sstevel@tonic-gate {
33400Sstevel@tonic-gate 	vmem_walk_t *vw = wsp->walk_data;
33410Sstevel@tonic-gate 	vmem_node_t *root = vw->vw_root;
33420Sstevel@tonic-gate 	int done;
33430Sstevel@tonic-gate 
33440Sstevel@tonic-gate 	if (root == NULL)
33450Sstevel@tonic-gate 		return;
33460Sstevel@tonic-gate 
33470Sstevel@tonic-gate 	if ((vw->vw_root = root->vn_children) != NULL)
33480Sstevel@tonic-gate 		vmem_walk_fini(wsp);
33490Sstevel@tonic-gate 
33500Sstevel@tonic-gate 	vw->vw_root = root->vn_sibling;
33510Sstevel@tonic-gate 	done = (root->vn_sibling == NULL && root->vn_parent == NULL);
33520Sstevel@tonic-gate 	mdb_free(root, sizeof (vmem_node_t));
33530Sstevel@tonic-gate 
33540Sstevel@tonic-gate 	if (done) {
33550Sstevel@tonic-gate 		mdb_free(vw, sizeof (vmem_walk_t));
33560Sstevel@tonic-gate 	} else {
33570Sstevel@tonic-gate 		vmem_walk_fini(wsp);
33580Sstevel@tonic-gate 	}
33590Sstevel@tonic-gate }
33600Sstevel@tonic-gate 
33610Sstevel@tonic-gate typedef struct vmem_seg_walk {
33620Sstevel@tonic-gate 	uint8_t vsw_type;
33630Sstevel@tonic-gate 	uintptr_t vsw_start;
33640Sstevel@tonic-gate 	uintptr_t vsw_current;
33650Sstevel@tonic-gate } vmem_seg_walk_t;
33660Sstevel@tonic-gate 
33670Sstevel@tonic-gate /*ARGSUSED*/
33680Sstevel@tonic-gate int
33690Sstevel@tonic-gate vmem_seg_walk_common_init(mdb_walk_state_t *wsp, uint8_t type, char *name)
33700Sstevel@tonic-gate {
33710Sstevel@tonic-gate 	vmem_seg_walk_t *vsw;
33720Sstevel@tonic-gate 
33730Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
33740Sstevel@tonic-gate 		mdb_warn("vmem_%s does not support global walks\n", name);
33750Sstevel@tonic-gate 		return (WALK_ERR);
33760Sstevel@tonic-gate 	}
33770Sstevel@tonic-gate 
33780Sstevel@tonic-gate 	wsp->walk_data = vsw = mdb_alloc(sizeof (vmem_seg_walk_t), UM_SLEEP);
33790Sstevel@tonic-gate 
33800Sstevel@tonic-gate 	vsw->vsw_type = type;
33810Sstevel@tonic-gate 	vsw->vsw_start = wsp->walk_addr + offsetof(vmem_t, vm_seg0);
33820Sstevel@tonic-gate 	vsw->vsw_current = vsw->vsw_start;
33830Sstevel@tonic-gate 
33840Sstevel@tonic-gate 	return (WALK_NEXT);
33850Sstevel@tonic-gate }
33860Sstevel@tonic-gate 
33870Sstevel@tonic-gate /*
33880Sstevel@tonic-gate  * vmem segments can't have type 0 (this should be added to vmem_impl.h).
33890Sstevel@tonic-gate  */
33900Sstevel@tonic-gate #define	VMEM_NONE	0
33910Sstevel@tonic-gate 
33920Sstevel@tonic-gate int
33930Sstevel@tonic-gate vmem_alloc_walk_init(mdb_walk_state_t *wsp)
33940Sstevel@tonic-gate {
33950Sstevel@tonic-gate 	return (vmem_seg_walk_common_init(wsp, VMEM_ALLOC, "alloc"));
33960Sstevel@tonic-gate }
33970Sstevel@tonic-gate 
33980Sstevel@tonic-gate int
33990Sstevel@tonic-gate vmem_free_walk_init(mdb_walk_state_t *wsp)
34000Sstevel@tonic-gate {
34010Sstevel@tonic-gate 	return (vmem_seg_walk_common_init(wsp, VMEM_FREE, "free"));
34020Sstevel@tonic-gate }
34030Sstevel@tonic-gate 
34040Sstevel@tonic-gate int
34050Sstevel@tonic-gate vmem_span_walk_init(mdb_walk_state_t *wsp)
34060Sstevel@tonic-gate {
34070Sstevel@tonic-gate 	return (vmem_seg_walk_common_init(wsp, VMEM_SPAN, "span"));
34080Sstevel@tonic-gate }
34090Sstevel@tonic-gate 
34100Sstevel@tonic-gate int
34110Sstevel@tonic-gate vmem_seg_walk_init(mdb_walk_state_t *wsp)
34120Sstevel@tonic-gate {
34130Sstevel@tonic-gate 	return (vmem_seg_walk_common_init(wsp, VMEM_NONE, "seg"));
34140Sstevel@tonic-gate }
34150Sstevel@tonic-gate 
34160Sstevel@tonic-gate int
34170Sstevel@tonic-gate vmem_seg_walk_step(mdb_walk_state_t *wsp)
34180Sstevel@tonic-gate {
34190Sstevel@tonic-gate 	vmem_seg_t seg;
34200Sstevel@tonic-gate 	vmem_seg_walk_t *vsw = wsp->walk_data;
34210Sstevel@tonic-gate 	uintptr_t addr = vsw->vsw_current;
34220Sstevel@tonic-gate 	static size_t seg_size = 0;
34230Sstevel@tonic-gate 	int rval;
34240Sstevel@tonic-gate 
34250Sstevel@tonic-gate 	if (!seg_size) {
34260Sstevel@tonic-gate 		if (mdb_readvar(&seg_size, "vmem_seg_size") == -1) {
34270Sstevel@tonic-gate 			mdb_warn("failed to read 'vmem_seg_size'");
34280Sstevel@tonic-gate 			seg_size = sizeof (vmem_seg_t);
34290Sstevel@tonic-gate 		}
34300Sstevel@tonic-gate 	}
34310Sstevel@tonic-gate 
34320Sstevel@tonic-gate 	if (seg_size < sizeof (seg))
34330Sstevel@tonic-gate 		bzero((caddr_t)&seg + seg_size, sizeof (seg) - seg_size);
34340Sstevel@tonic-gate 
34350Sstevel@tonic-gate 	if (mdb_vread(&seg, seg_size, addr) == -1) {
34360Sstevel@tonic-gate 		mdb_warn("couldn't read vmem_seg at %p", addr);
34370Sstevel@tonic-gate 		return (WALK_ERR);
34380Sstevel@tonic-gate 	}
34390Sstevel@tonic-gate 
34400Sstevel@tonic-gate 	vsw->vsw_current = (uintptr_t)seg.vs_anext;
34410Sstevel@tonic-gate 	if (vsw->vsw_type != VMEM_NONE && seg.vs_type != vsw->vsw_type) {
34420Sstevel@tonic-gate 		rval = WALK_NEXT;
34430Sstevel@tonic-gate 	} else {
34440Sstevel@tonic-gate 		rval = wsp->walk_callback(addr, &seg, wsp->walk_cbdata);
34450Sstevel@tonic-gate 	}
34460Sstevel@tonic-gate 
34470Sstevel@tonic-gate 	if (vsw->vsw_current == vsw->vsw_start)
34480Sstevel@tonic-gate 		return (WALK_DONE);
34490Sstevel@tonic-gate 
34500Sstevel@tonic-gate 	return (rval);
34510Sstevel@tonic-gate }
34520Sstevel@tonic-gate 
34530Sstevel@tonic-gate void
34540Sstevel@tonic-gate vmem_seg_walk_fini(mdb_walk_state_t *wsp)
34550Sstevel@tonic-gate {
34560Sstevel@tonic-gate 	vmem_seg_walk_t *vsw = wsp->walk_data;
34570Sstevel@tonic-gate 
34580Sstevel@tonic-gate 	mdb_free(vsw, sizeof (vmem_seg_walk_t));
34590Sstevel@tonic-gate }
34600Sstevel@tonic-gate 
34610Sstevel@tonic-gate #define	VMEM_NAMEWIDTH	22
34620Sstevel@tonic-gate 
34630Sstevel@tonic-gate int
34640Sstevel@tonic-gate vmem(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
34650Sstevel@tonic-gate {
34660Sstevel@tonic-gate 	vmem_t v, parent;
34670Sstevel@tonic-gate 	vmem_kstat_t *vkp = &v.vm_kstat;
34680Sstevel@tonic-gate 	uintptr_t paddr;
34690Sstevel@tonic-gate 	int ident = 0;
34700Sstevel@tonic-gate 	char c[VMEM_NAMEWIDTH];
34710Sstevel@tonic-gate 
34720Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
34730Sstevel@tonic-gate 		if (mdb_walk_dcmd("vmem", "vmem", argc, argv) == -1) {
34740Sstevel@tonic-gate 			mdb_warn("can't walk vmem");
34750Sstevel@tonic-gate 			return (DCMD_ERR);
34760Sstevel@tonic-gate 		}
34770Sstevel@tonic-gate 		return (DCMD_OK);
34780Sstevel@tonic-gate 	}
34790Sstevel@tonic-gate 
34800Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags))
34810Sstevel@tonic-gate 		mdb_printf("%-?s %-*s %10s %12s %9s %5s\n",
34820Sstevel@tonic-gate 		    "ADDR", VMEM_NAMEWIDTH, "NAME", "INUSE",
34830Sstevel@tonic-gate 		    "TOTAL", "SUCCEED", "FAIL");
34840Sstevel@tonic-gate 
34850Sstevel@tonic-gate 	if (mdb_vread(&v, sizeof (v), addr) == -1) {
34860Sstevel@tonic-gate 		mdb_warn("couldn't read vmem at %p", addr);
34870Sstevel@tonic-gate 		return (DCMD_ERR);
34880Sstevel@tonic-gate 	}
34890Sstevel@tonic-gate 
34900Sstevel@tonic-gate 	for (paddr = (uintptr_t)v.vm_source; paddr != NULL; ident += 2) {
34910Sstevel@tonic-gate 		if (mdb_vread(&parent, sizeof (parent), paddr) == -1) {
34920Sstevel@tonic-gate 			mdb_warn("couldn't trace %p's ancestry", addr);
34930Sstevel@tonic-gate 			ident = 0;
34940Sstevel@tonic-gate 			break;
34950Sstevel@tonic-gate 		}
34960Sstevel@tonic-gate 		paddr = (uintptr_t)parent.vm_source;
34970Sstevel@tonic-gate 	}
34980Sstevel@tonic-gate 
34990Sstevel@tonic-gate 	(void) mdb_snprintf(c, VMEM_NAMEWIDTH, "%*s%s", ident, "", v.vm_name);
35000Sstevel@tonic-gate 
35010Sstevel@tonic-gate 	mdb_printf("%0?p %-*s %10llu %12llu %9llu %5llu\n",
35020Sstevel@tonic-gate 	    addr, VMEM_NAMEWIDTH, c,
35030Sstevel@tonic-gate 	    vkp->vk_mem_inuse.value.ui64, vkp->vk_mem_total.value.ui64,
35040Sstevel@tonic-gate 	    vkp->vk_alloc.value.ui64, vkp->vk_fail.value.ui64);
35050Sstevel@tonic-gate 
35060Sstevel@tonic-gate 	return (DCMD_OK);
35070Sstevel@tonic-gate }
35080Sstevel@tonic-gate 
35090Sstevel@tonic-gate void
35100Sstevel@tonic-gate vmem_seg_help(void)
35110Sstevel@tonic-gate {
35120Sstevel@tonic-gate 	mdb_printf("%s\n",
35130Sstevel@tonic-gate "Display the contents of vmem_seg_ts, with optional filtering.\n"
35140Sstevel@tonic-gate "\n"
35150Sstevel@tonic-gate "A vmem_seg_t represents a range of addresses (or arbitrary numbers),\n"
35160Sstevel@tonic-gate "representing a single chunk of data.  Only ALLOC segments have debugging\n"
35170Sstevel@tonic-gate "information.\n");
35180Sstevel@tonic-gate 	mdb_dec_indent(2);
35190Sstevel@tonic-gate 	mdb_printf("%<b>OPTIONS%</b>\n");
35200Sstevel@tonic-gate 	mdb_inc_indent(2);
35210Sstevel@tonic-gate 	mdb_printf("%s",
35220Sstevel@tonic-gate "  -v    Display the full content of the vmem_seg, including its stack trace\n"
35230Sstevel@tonic-gate "  -s    report the size of the segment, instead of the end address\n"
35240Sstevel@tonic-gate "  -c caller\n"
35250Sstevel@tonic-gate "        filter out segments without the function/PC in their stack trace\n"
35260Sstevel@tonic-gate "  -e earliest\n"
35270Sstevel@tonic-gate "        filter out segments timestamped before earliest\n"
35280Sstevel@tonic-gate "  -l latest\n"
35290Sstevel@tonic-gate "        filter out segments timestamped after latest\n"
35300Sstevel@tonic-gate "  -m minsize\n"
35310Sstevel@tonic-gate "        filer out segments smaller than minsize\n"
35320Sstevel@tonic-gate "  -M maxsize\n"
35330Sstevel@tonic-gate "        filer out segments larger than maxsize\n"
35340Sstevel@tonic-gate "  -t thread\n"
35350Sstevel@tonic-gate "        filter out segments not involving thread\n"
35360Sstevel@tonic-gate "  -T type\n"
35370Sstevel@tonic-gate "        filter out segments not of type 'type'\n"
35380Sstevel@tonic-gate "        type is one of: ALLOC/FREE/SPAN/ROTOR/WALKER\n");
35390Sstevel@tonic-gate }
35400Sstevel@tonic-gate 
35410Sstevel@tonic-gate /*ARGSUSED*/
35420Sstevel@tonic-gate int
35430Sstevel@tonic-gate vmem_seg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
35440Sstevel@tonic-gate {
35450Sstevel@tonic-gate 	vmem_seg_t vs;
35460Sstevel@tonic-gate 	pc_t *stk = vs.vs_stack;
35470Sstevel@tonic-gate 	uintptr_t sz;
35480Sstevel@tonic-gate 	uint8_t t;
35490Sstevel@tonic-gate 	const char *type = NULL;
35500Sstevel@tonic-gate 	GElf_Sym sym;
35510Sstevel@tonic-gate 	char c[MDB_SYM_NAMLEN];
35520Sstevel@tonic-gate 	int no_debug;
35530Sstevel@tonic-gate 	int i;
35540Sstevel@tonic-gate 	int depth;
35550Sstevel@tonic-gate 	uintptr_t laddr, haddr;
35560Sstevel@tonic-gate 
35570Sstevel@tonic-gate 	uintptr_t caller = NULL, thread = NULL;
35580Sstevel@tonic-gate 	uintptr_t minsize = 0, maxsize = 0;
35590Sstevel@tonic-gate 
35600Sstevel@tonic-gate 	hrtime_t earliest = 0, latest = 0;
35610Sstevel@tonic-gate 
35620Sstevel@tonic-gate 	uint_t size = 0;
35630Sstevel@tonic-gate 	uint_t verbose = 0;
35640Sstevel@tonic-gate 
35650Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
35660Sstevel@tonic-gate 		return (DCMD_USAGE);
35670Sstevel@tonic-gate 
35680Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
35690Sstevel@tonic-gate 	    'c', MDB_OPT_UINTPTR, &caller,
35700Sstevel@tonic-gate 	    'e', MDB_OPT_UINT64, &earliest,
35710Sstevel@tonic-gate 	    'l', MDB_OPT_UINT64, &latest,
35720Sstevel@tonic-gate 	    's', MDB_OPT_SETBITS, TRUE, &size,
35730Sstevel@tonic-gate 	    'm', MDB_OPT_UINTPTR, &minsize,
35740Sstevel@tonic-gate 	    'M', MDB_OPT_UINTPTR, &maxsize,
35750Sstevel@tonic-gate 	    't', MDB_OPT_UINTPTR, &thread,
35760Sstevel@tonic-gate 	    'T', MDB_OPT_STR, &type,
35770Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
35780Sstevel@tonic-gate 	    NULL) != argc)
35790Sstevel@tonic-gate 		return (DCMD_USAGE);
35800Sstevel@tonic-gate 
35810Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
35820Sstevel@tonic-gate 		if (verbose) {
35830Sstevel@tonic-gate 			mdb_printf("%16s %4s %16s %16s %16s\n"
35840Sstevel@tonic-gate 			    "%<u>%16s %4s %16s %16s %16s%</u>\n",
35850Sstevel@tonic-gate 			    "ADDR", "TYPE", "START", "END", "SIZE",
35860Sstevel@tonic-gate 			    "", "", "THREAD", "TIMESTAMP", "");
35870Sstevel@tonic-gate 		} else {
35880Sstevel@tonic-gate 			mdb_printf("%?s %4s %?s %?s %s\n", "ADDR", "TYPE",
35890Sstevel@tonic-gate 			    "START", size? "SIZE" : "END", "WHO");
35900Sstevel@tonic-gate 		}
35910Sstevel@tonic-gate 	}
35920Sstevel@tonic-gate 
35930Sstevel@tonic-gate 	if (mdb_vread(&vs, sizeof (vs), addr) == -1) {
35940Sstevel@tonic-gate 		mdb_warn("couldn't read vmem_seg at %p", addr);
35950Sstevel@tonic-gate 		return (DCMD_ERR);
35960Sstevel@tonic-gate 	}
35970Sstevel@tonic-gate 
35980Sstevel@tonic-gate 	if (type != NULL) {
35990Sstevel@tonic-gate 		if (strcmp(type, "ALLC") == 0 || strcmp(type, "ALLOC") == 0)
36000Sstevel@tonic-gate 			t = VMEM_ALLOC;
36010Sstevel@tonic-gate 		else if (strcmp(type, "FREE") == 0)
36020Sstevel@tonic-gate 			t = VMEM_FREE;
36030Sstevel@tonic-gate 		else if (strcmp(type, "SPAN") == 0)
36040Sstevel@tonic-gate 			t = VMEM_SPAN;
36050Sstevel@tonic-gate 		else if (strcmp(type, "ROTR") == 0 ||
36060Sstevel@tonic-gate 		    strcmp(type, "ROTOR") == 0)
36070Sstevel@tonic-gate 			t = VMEM_ROTOR;
36080Sstevel@tonic-gate 		else if (strcmp(type, "WLKR") == 0 ||
36090Sstevel@tonic-gate 		    strcmp(type, "WALKER") == 0)
36100Sstevel@tonic-gate 			t = VMEM_WALKER;
36110Sstevel@tonic-gate 		else {
36120Sstevel@tonic-gate 			mdb_warn("\"%s\" is not a recognized vmem_seg type\n",
36130Sstevel@tonic-gate 			    type);
36140Sstevel@tonic-gate 			return (DCMD_ERR);
36150Sstevel@tonic-gate 		}
36160Sstevel@tonic-gate 
36170Sstevel@tonic-gate 		if (vs.vs_type != t)
36180Sstevel@tonic-gate 			return (DCMD_OK);
36190Sstevel@tonic-gate 	}
36200Sstevel@tonic-gate 
36210Sstevel@tonic-gate 	sz = vs.vs_end - vs.vs_start;
36220Sstevel@tonic-gate 
36230Sstevel@tonic-gate 	if (minsize != 0 && sz < minsize)
36240Sstevel@tonic-gate 		return (DCMD_OK);
36250Sstevel@tonic-gate 
36260Sstevel@tonic-gate 	if (maxsize != 0 && sz > maxsize)
36270Sstevel@tonic-gate 		return (DCMD_OK);
36280Sstevel@tonic-gate 
36290Sstevel@tonic-gate 	t = vs.vs_type;
36300Sstevel@tonic-gate 	depth = vs.vs_depth;
36310Sstevel@tonic-gate 
36320Sstevel@tonic-gate 	/*
36330Sstevel@tonic-gate 	 * debug info, when present, is only accurate for VMEM_ALLOC segments
36340Sstevel@tonic-gate 	 */
36350Sstevel@tonic-gate 	no_debug = (t != VMEM_ALLOC) ||
36360Sstevel@tonic-gate 	    (depth == 0 || depth > VMEM_STACK_DEPTH);
36370Sstevel@tonic-gate 
36380Sstevel@tonic-gate 	if (no_debug) {
36390Sstevel@tonic-gate 		if (caller != NULL || thread != NULL || earliest != 0 ||
36400Sstevel@tonic-gate 		    latest != 0)
36410Sstevel@tonic-gate 			return (DCMD_OK);		/* not enough info */
36420Sstevel@tonic-gate 	} else {
36430Sstevel@tonic-gate 		if (caller != NULL) {
36440Sstevel@tonic-gate 			laddr = caller;
36450Sstevel@tonic-gate 			haddr = caller + sizeof (caller);
36460Sstevel@tonic-gate 
36470Sstevel@tonic-gate 			if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c,
36480Sstevel@tonic-gate 			    sizeof (c), &sym) != -1 &&
36490Sstevel@tonic-gate 			    caller == (uintptr_t)sym.st_value) {
36500Sstevel@tonic-gate 				/*
36510Sstevel@tonic-gate 				 * We were provided an exact symbol value; any
36520Sstevel@tonic-gate 				 * address in the function is valid.
36530Sstevel@tonic-gate 				 */
36540Sstevel@tonic-gate 				laddr = (uintptr_t)sym.st_value;
36550Sstevel@tonic-gate 				haddr = (uintptr_t)sym.st_value + sym.st_size;
36560Sstevel@tonic-gate 			}
36570Sstevel@tonic-gate 
36580Sstevel@tonic-gate 			for (i = 0; i < depth; i++)
36590Sstevel@tonic-gate 				if (vs.vs_stack[i] >= laddr &&
36600Sstevel@tonic-gate 				    vs.vs_stack[i] < haddr)
36610Sstevel@tonic-gate 					break;
36620Sstevel@tonic-gate 
36630Sstevel@tonic-gate 			if (i == depth)
36640Sstevel@tonic-gate 				return (DCMD_OK);
36650Sstevel@tonic-gate 		}
36660Sstevel@tonic-gate 
36670Sstevel@tonic-gate 		if (thread != NULL && (uintptr_t)vs.vs_thread != thread)
36680Sstevel@tonic-gate 			return (DCMD_OK);
36690Sstevel@tonic-gate 
36700Sstevel@tonic-gate 		if (earliest != 0 && vs.vs_timestamp < earliest)
36710Sstevel@tonic-gate 			return (DCMD_OK);
36720Sstevel@tonic-gate 
36730Sstevel@tonic-gate 		if (latest != 0 && vs.vs_timestamp > latest)
36740Sstevel@tonic-gate 			return (DCMD_OK);
36750Sstevel@tonic-gate 	}
36760Sstevel@tonic-gate 
36770Sstevel@tonic-gate 	type = (t == VMEM_ALLOC ? "ALLC" :
36780Sstevel@tonic-gate 	    t == VMEM_FREE ? "FREE" :
36790Sstevel@tonic-gate 	    t == VMEM_SPAN ? "SPAN" :
36800Sstevel@tonic-gate 	    t == VMEM_ROTOR ? "ROTR" :
36810Sstevel@tonic-gate 	    t == VMEM_WALKER ? "WLKR" :
36820Sstevel@tonic-gate 	    "????");
36830Sstevel@tonic-gate 
36840Sstevel@tonic-gate 	if (flags & DCMD_PIPE_OUT) {
36850Sstevel@tonic-gate 		mdb_printf("%#lr\n", addr);
36860Sstevel@tonic-gate 		return (DCMD_OK);
36870Sstevel@tonic-gate 	}
36880Sstevel@tonic-gate 
36890Sstevel@tonic-gate 	if (verbose) {
36900Sstevel@tonic-gate 		mdb_printf("%<b>%16p%</b> %4s %16p %16p %16d\n",
36910Sstevel@tonic-gate 		    addr, type, vs.vs_start, vs.vs_end, sz);
36920Sstevel@tonic-gate 
36930Sstevel@tonic-gate 		if (no_debug)
36940Sstevel@tonic-gate 			return (DCMD_OK);
36950Sstevel@tonic-gate 
36960Sstevel@tonic-gate 		mdb_printf("%16s %4s %16p %16llx\n",
36970Sstevel@tonic-gate 		    "", "", vs.vs_thread, vs.vs_timestamp);
36980Sstevel@tonic-gate 
36990Sstevel@tonic-gate 		mdb_inc_indent(17);
37000Sstevel@tonic-gate 		for (i = 0; i < depth; i++) {
37010Sstevel@tonic-gate 			mdb_printf("%a\n", stk[i]);
37020Sstevel@tonic-gate 		}
37030Sstevel@tonic-gate 		mdb_dec_indent(17);
37040Sstevel@tonic-gate 		mdb_printf("\n");
37050Sstevel@tonic-gate 	} else {
37060Sstevel@tonic-gate 		mdb_printf("%0?p %4s %0?p %0?p", addr, type,
37070Sstevel@tonic-gate 		    vs.vs_start, size? sz : vs.vs_end);
37080Sstevel@tonic-gate 
37090Sstevel@tonic-gate 		if (no_debug) {
37100Sstevel@tonic-gate 			mdb_printf("\n");
37110Sstevel@tonic-gate 			return (DCMD_OK);
37120Sstevel@tonic-gate 		}
37130Sstevel@tonic-gate 
37140Sstevel@tonic-gate 		for (i = 0; i < depth; i++) {
37150Sstevel@tonic-gate 			if (mdb_lookup_by_addr(stk[i], MDB_SYM_FUZZY,
37160Sstevel@tonic-gate 			    c, sizeof (c), &sym) == -1)
37170Sstevel@tonic-gate 				continue;
37180Sstevel@tonic-gate 			if (strncmp(c, "vmem_", 5) == 0)
37190Sstevel@tonic-gate 				continue;
37200Sstevel@tonic-gate 			break;
37210Sstevel@tonic-gate 		}
37220Sstevel@tonic-gate 		mdb_printf(" %a\n", stk[i]);
37230Sstevel@tonic-gate 	}
37240Sstevel@tonic-gate 	return (DCMD_OK);
37250Sstevel@tonic-gate }
37260Sstevel@tonic-gate 
37270Sstevel@tonic-gate typedef struct kmalog_data {
37280Sstevel@tonic-gate 	uintptr_t	kma_addr;
37290Sstevel@tonic-gate 	hrtime_t	kma_newest;
37300Sstevel@tonic-gate } kmalog_data_t;
37310Sstevel@tonic-gate 
37320Sstevel@tonic-gate /*ARGSUSED*/
37330Sstevel@tonic-gate static int
37340Sstevel@tonic-gate showbc(uintptr_t addr, const kmem_bufctl_audit_t *bcp, kmalog_data_t *kma)
37350Sstevel@tonic-gate {
37360Sstevel@tonic-gate 	char name[KMEM_CACHE_NAMELEN + 1];
37370Sstevel@tonic-gate 	hrtime_t delta;
37380Sstevel@tonic-gate 	int i, depth;
37390Sstevel@tonic-gate 	size_t bufsize;
37400Sstevel@tonic-gate 
37410Sstevel@tonic-gate 	if (bcp->bc_timestamp == 0)
37420Sstevel@tonic-gate 		return (WALK_DONE);
37430Sstevel@tonic-gate 
37440Sstevel@tonic-gate 	if (kma->kma_newest == 0)
37450Sstevel@tonic-gate 		kma->kma_newest = bcp->bc_timestamp;
37460Sstevel@tonic-gate 
37470Sstevel@tonic-gate 	if (kma->kma_addr) {
37480Sstevel@tonic-gate 		if (mdb_vread(&bufsize, sizeof (bufsize),
37490Sstevel@tonic-gate 		    (uintptr_t)&bcp->bc_cache->cache_bufsize) == -1) {
37500Sstevel@tonic-gate 			mdb_warn(
37510Sstevel@tonic-gate 			    "failed to read cache_bufsize for cache at %p",
37520Sstevel@tonic-gate 			    bcp->bc_cache);
37530Sstevel@tonic-gate 			return (WALK_ERR);
37540Sstevel@tonic-gate 		}
37550Sstevel@tonic-gate 
37560Sstevel@tonic-gate 		if (kma->kma_addr < (uintptr_t)bcp->bc_addr ||
37570Sstevel@tonic-gate 		    kma->kma_addr >= (uintptr_t)bcp->bc_addr + bufsize)
37580Sstevel@tonic-gate 			return (WALK_NEXT);
37590Sstevel@tonic-gate 	}
37600Sstevel@tonic-gate 
37610Sstevel@tonic-gate 	delta = kma->kma_newest - bcp->bc_timestamp;
37620Sstevel@tonic-gate 	depth = MIN(bcp->bc_depth, KMEM_STACK_DEPTH);
37630Sstevel@tonic-gate 
37640Sstevel@tonic-gate 	if (mdb_readstr(name, sizeof (name), (uintptr_t)
37650Sstevel@tonic-gate 	    &bcp->bc_cache->cache_name) <= 0)
37660Sstevel@tonic-gate 		(void) mdb_snprintf(name, sizeof (name), "%a", bcp->bc_cache);
37670Sstevel@tonic-gate 
37680Sstevel@tonic-gate 	mdb_printf("\nT-%lld.%09lld  addr=%p  %s\n",
37690Sstevel@tonic-gate 	    delta / NANOSEC, delta % NANOSEC, bcp->bc_addr, name);
37700Sstevel@tonic-gate 
37710Sstevel@tonic-gate 	for (i = 0; i < depth; i++)
37720Sstevel@tonic-gate 		mdb_printf("\t %a\n", bcp->bc_stack[i]);
37730Sstevel@tonic-gate 
37740Sstevel@tonic-gate 	return (WALK_NEXT);
37750Sstevel@tonic-gate }
37760Sstevel@tonic-gate 
37770Sstevel@tonic-gate int
37780Sstevel@tonic-gate kmalog(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
37790Sstevel@tonic-gate {
37800Sstevel@tonic-gate 	const char *logname = "kmem_transaction_log";
37810Sstevel@tonic-gate 	kmalog_data_t kma;
37820Sstevel@tonic-gate 
37830Sstevel@tonic-gate 	if (argc > 1)
37840Sstevel@tonic-gate 		return (DCMD_USAGE);
37850Sstevel@tonic-gate 
37860Sstevel@tonic-gate 	kma.kma_newest = 0;
37870Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
37880Sstevel@tonic-gate 		kma.kma_addr = addr;
37890Sstevel@tonic-gate 	else
37900Sstevel@tonic-gate 		kma.kma_addr = NULL;
37910Sstevel@tonic-gate 
37920Sstevel@tonic-gate 	if (argc > 0) {
37930Sstevel@tonic-gate 		if (argv->a_type != MDB_TYPE_STRING)
37940Sstevel@tonic-gate 			return (DCMD_USAGE);
37950Sstevel@tonic-gate 		if (strcmp(argv->a_un.a_str, "fail") == 0)
37960Sstevel@tonic-gate 			logname = "kmem_failure_log";
37970Sstevel@tonic-gate 		else if (strcmp(argv->a_un.a_str, "slab") == 0)
37980Sstevel@tonic-gate 			logname = "kmem_slab_log";
37990Sstevel@tonic-gate 		else
38000Sstevel@tonic-gate 			return (DCMD_USAGE);
38010Sstevel@tonic-gate 	}
38020Sstevel@tonic-gate 
38030Sstevel@tonic-gate 	if (mdb_readvar(&addr, logname) == -1) {
38040Sstevel@tonic-gate 		mdb_warn("failed to read %s log header pointer");
38050Sstevel@tonic-gate 		return (DCMD_ERR);
38060Sstevel@tonic-gate 	}
38070Sstevel@tonic-gate 
38080Sstevel@tonic-gate 	if (mdb_pwalk("kmem_log", (mdb_walk_cb_t)showbc, &kma, addr) == -1) {
38090Sstevel@tonic-gate 		mdb_warn("failed to walk kmem log");
38100Sstevel@tonic-gate 		return (DCMD_ERR);
38110Sstevel@tonic-gate 	}
38120Sstevel@tonic-gate 
38130Sstevel@tonic-gate 	return (DCMD_OK);
38140Sstevel@tonic-gate }
38150Sstevel@tonic-gate 
38160Sstevel@tonic-gate /*
38170Sstevel@tonic-gate  * As the final lure for die-hard crash(1M) users, we provide ::kmausers here.
38180Sstevel@tonic-gate  * The first piece is a structure which we use to accumulate kmem_cache_t
38190Sstevel@tonic-gate  * addresses of interest.  The kmc_add is used as a callback for the kmem_cache
38200Sstevel@tonic-gate  * walker; we either add all caches, or ones named explicitly as arguments.
38210Sstevel@tonic-gate  */
38220Sstevel@tonic-gate 
38230Sstevel@tonic-gate typedef struct kmclist {
38240Sstevel@tonic-gate 	const char *kmc_name;			/* Name to match (or NULL) */
38250Sstevel@tonic-gate 	uintptr_t *kmc_caches;			/* List of kmem_cache_t addrs */
38260Sstevel@tonic-gate 	int kmc_nelems;				/* Num entries in kmc_caches */
38270Sstevel@tonic-gate 	int kmc_size;				/* Size of kmc_caches array */
38280Sstevel@tonic-gate } kmclist_t;
38290Sstevel@tonic-gate 
38300Sstevel@tonic-gate static int
38310Sstevel@tonic-gate kmc_add(uintptr_t addr, const kmem_cache_t *cp, kmclist_t *kmc)
38320Sstevel@tonic-gate {
38330Sstevel@tonic-gate 	void *p;
38340Sstevel@tonic-gate 	int s;
38350Sstevel@tonic-gate 
38360Sstevel@tonic-gate 	if (kmc->kmc_name == NULL ||
38370Sstevel@tonic-gate 	    strcmp(cp->cache_name, kmc->kmc_name) == 0) {
38380Sstevel@tonic-gate 		/*
38390Sstevel@tonic-gate 		 * If we have a match, grow our array (if necessary), and then
38400Sstevel@tonic-gate 		 * add the virtual address of the matching cache to our list.
38410Sstevel@tonic-gate 		 */
38420Sstevel@tonic-gate 		if (kmc->kmc_nelems >= kmc->kmc_size) {
38430Sstevel@tonic-gate 			s = kmc->kmc_size ? kmc->kmc_size * 2 : 256;
38440Sstevel@tonic-gate 			p = mdb_alloc(sizeof (uintptr_t) * s, UM_SLEEP | UM_GC);
38450Sstevel@tonic-gate 
38460Sstevel@tonic-gate 			bcopy(kmc->kmc_caches, p,
38470Sstevel@tonic-gate 			    sizeof (uintptr_t) * kmc->kmc_size);
38480Sstevel@tonic-gate 
38490Sstevel@tonic-gate 			kmc->kmc_caches = p;
38500Sstevel@tonic-gate 			kmc->kmc_size = s;
38510Sstevel@tonic-gate 		}
38520Sstevel@tonic-gate 
38530Sstevel@tonic-gate 		kmc->kmc_caches[kmc->kmc_nelems++] = addr;
38540Sstevel@tonic-gate 		return (kmc->kmc_name ? WALK_DONE : WALK_NEXT);
38550Sstevel@tonic-gate 	}
38560Sstevel@tonic-gate 
38570Sstevel@tonic-gate 	return (WALK_NEXT);
38580Sstevel@tonic-gate }
38590Sstevel@tonic-gate 
38600Sstevel@tonic-gate /*
38610Sstevel@tonic-gate  * The second piece of ::kmausers is a hash table of allocations.  Each
38620Sstevel@tonic-gate  * allocation owner is identified by its stack trace and data_size.  We then
38630Sstevel@tonic-gate  * track the total bytes of all such allocations, and the number of allocations
38640Sstevel@tonic-gate  * to report at the end.  Once we have a list of caches, we walk through the
38650Sstevel@tonic-gate  * allocated bufctls of each, and update our hash table accordingly.
38660Sstevel@tonic-gate  */
38670Sstevel@tonic-gate 
38680Sstevel@tonic-gate typedef struct kmowner {
38690Sstevel@tonic-gate 	struct kmowner *kmo_head;		/* First hash elt in bucket */
38700Sstevel@tonic-gate 	struct kmowner *kmo_next;		/* Next hash elt in chain */
38710Sstevel@tonic-gate 	size_t kmo_signature;			/* Hash table signature */
38720Sstevel@tonic-gate 	uint_t kmo_num;				/* Number of allocations */
38730Sstevel@tonic-gate 	size_t kmo_data_size;			/* Size of each allocation */
38740Sstevel@tonic-gate 	size_t kmo_total_size;			/* Total bytes of allocation */
38750Sstevel@tonic-gate 	int kmo_depth;				/* Depth of stack trace */
38760Sstevel@tonic-gate 	uintptr_t kmo_stack[KMEM_STACK_DEPTH];	/* Stack trace */
38770Sstevel@tonic-gate } kmowner_t;
38780Sstevel@tonic-gate 
38790Sstevel@tonic-gate typedef struct kmusers {
38800Sstevel@tonic-gate 	uintptr_t kmu_addr;			/* address of interest */
38810Sstevel@tonic-gate 	const kmem_cache_t *kmu_cache;		/* Current kmem cache */
38820Sstevel@tonic-gate 	kmowner_t *kmu_hash;			/* Hash table of owners */
38830Sstevel@tonic-gate 	int kmu_nelems;				/* Number of entries in use */
38840Sstevel@tonic-gate 	int kmu_size;				/* Total number of entries */
38850Sstevel@tonic-gate } kmusers_t;
38860Sstevel@tonic-gate 
38870Sstevel@tonic-gate static void
38880Sstevel@tonic-gate kmu_add(kmusers_t *kmu, const kmem_bufctl_audit_t *bcp,
38890Sstevel@tonic-gate     size_t size, size_t data_size)
38900Sstevel@tonic-gate {
38910Sstevel@tonic-gate 	int i, depth = MIN(bcp->bc_depth, KMEM_STACK_DEPTH);
38920Sstevel@tonic-gate 	size_t bucket, signature = data_size;
38930Sstevel@tonic-gate 	kmowner_t *kmo, *kmoend;
38940Sstevel@tonic-gate 
38950Sstevel@tonic-gate 	/*
38960Sstevel@tonic-gate 	 * If the hash table is full, double its size and rehash everything.
38970Sstevel@tonic-gate 	 */
38980Sstevel@tonic-gate 	if (kmu->kmu_nelems >= kmu->kmu_size) {
38990Sstevel@tonic-gate 		int s = kmu->kmu_size ? kmu->kmu_size * 2 : 1024;
39000Sstevel@tonic-gate 
39010Sstevel@tonic-gate 		kmo = mdb_alloc(sizeof (kmowner_t) * s, UM_SLEEP | UM_GC);
39020Sstevel@tonic-gate 		bcopy(kmu->kmu_hash, kmo, sizeof (kmowner_t) * kmu->kmu_size);
39030Sstevel@tonic-gate 		kmu->kmu_hash = kmo;
39040Sstevel@tonic-gate 		kmu->kmu_size = s;
39050Sstevel@tonic-gate 
39060Sstevel@tonic-gate 		kmoend = kmu->kmu_hash + kmu->kmu_size;
39070Sstevel@tonic-gate 		for (kmo = kmu->kmu_hash; kmo < kmoend; kmo++)
39080Sstevel@tonic-gate 			kmo->kmo_head = NULL;
39090Sstevel@tonic-gate 
39100Sstevel@tonic-gate 		kmoend = kmu->kmu_hash + kmu->kmu_nelems;
39110Sstevel@tonic-gate 		for (kmo = kmu->kmu_hash; kmo < kmoend; kmo++) {
39120Sstevel@tonic-gate 			bucket = kmo->kmo_signature & (kmu->kmu_size - 1);
39130Sstevel@tonic-gate 			kmo->kmo_next = kmu->kmu_hash[bucket].kmo_head;
39140Sstevel@tonic-gate 			kmu->kmu_hash[bucket].kmo_head = kmo;
39150Sstevel@tonic-gate 		}
39160Sstevel@tonic-gate 	}
39170Sstevel@tonic-gate 
39180Sstevel@tonic-gate 	/*
39190Sstevel@tonic-gate 	 * Finish computing the hash signature from the stack trace, and then
39200Sstevel@tonic-gate 	 * see if the owner is in the hash table.  If so, update our stats.
39210Sstevel@tonic-gate 	 */
39220Sstevel@tonic-gate 	for (i = 0; i < depth; i++)
39230Sstevel@tonic-gate 		signature += bcp->bc_stack[i];
39240Sstevel@tonic-gate 
39250Sstevel@tonic-gate 	bucket = signature & (kmu->kmu_size - 1);
39260Sstevel@tonic-gate 
39270Sstevel@tonic-gate 	for (kmo = kmu->kmu_hash[bucket].kmo_head; kmo; kmo = kmo->kmo_next) {
39280Sstevel@tonic-gate 		if (kmo->kmo_signature == signature) {
39290Sstevel@tonic-gate 			size_t difference = 0;
39300Sstevel@tonic-gate 
39310Sstevel@tonic-gate 			difference |= kmo->kmo_data_size - data_size;
39320Sstevel@tonic-gate 			difference |= kmo->kmo_depth - depth;
39330Sstevel@tonic-gate 
39340Sstevel@tonic-gate 			for (i = 0; i < depth; i++) {
39350Sstevel@tonic-gate 				difference |= kmo->kmo_stack[i] -
39360Sstevel@tonic-gate 				    bcp->bc_stack[i];
39370Sstevel@tonic-gate 			}
39380Sstevel@tonic-gate 
39390Sstevel@tonic-gate 			if (difference == 0) {
39400Sstevel@tonic-gate 				kmo->kmo_total_size += size;
39410Sstevel@tonic-gate 				kmo->kmo_num++;
39420Sstevel@tonic-gate 				return;
39430Sstevel@tonic-gate 			}
39440Sstevel@tonic-gate 		}
39450Sstevel@tonic-gate 	}
39460Sstevel@tonic-gate 
39470Sstevel@tonic-gate 	/*
39480Sstevel@tonic-gate 	 * If the owner is not yet hashed, grab the next element and fill it
39490Sstevel@tonic-gate 	 * in based on the allocation information.
39500Sstevel@tonic-gate 	 */
39510Sstevel@tonic-gate 	kmo = &kmu->kmu_hash[kmu->kmu_nelems++];
39520Sstevel@tonic-gate 	kmo->kmo_next = kmu->kmu_hash[bucket].kmo_head;
39530Sstevel@tonic-gate 	kmu->kmu_hash[bucket].kmo_head = kmo;
39540Sstevel@tonic-gate 
39550Sstevel@tonic-gate 	kmo->kmo_signature = signature;
39560Sstevel@tonic-gate 	kmo->kmo_num = 1;
39570Sstevel@tonic-gate 	kmo->kmo_data_size = data_size;
39580Sstevel@tonic-gate 	kmo->kmo_total_size = size;
39590Sstevel@tonic-gate 	kmo->kmo_depth = depth;
39600Sstevel@tonic-gate 
39610Sstevel@tonic-gate 	for (i = 0; i < depth; i++)
39620Sstevel@tonic-gate 		kmo->kmo_stack[i] = bcp->bc_stack[i];
39630Sstevel@tonic-gate }
39640Sstevel@tonic-gate 
39650Sstevel@tonic-gate /*
39660Sstevel@tonic-gate  * When ::kmausers is invoked without the -f flag, we simply update our hash
39670Sstevel@tonic-gate  * table with the information from each allocated bufctl.
39680Sstevel@tonic-gate  */
39690Sstevel@tonic-gate /*ARGSUSED*/
39700Sstevel@tonic-gate static int
39710Sstevel@tonic-gate kmause1(uintptr_t addr, const kmem_bufctl_audit_t *bcp, kmusers_t *kmu)
39720Sstevel@tonic-gate {
39730Sstevel@tonic-gate 	const kmem_cache_t *cp = kmu->kmu_cache;
39740Sstevel@tonic-gate 
39750Sstevel@tonic-gate 	kmu_add(kmu, bcp, cp->cache_bufsize, cp->cache_bufsize);
39760Sstevel@tonic-gate 	return (WALK_NEXT);
39770Sstevel@tonic-gate }
39780Sstevel@tonic-gate 
39790Sstevel@tonic-gate /*
39800Sstevel@tonic-gate  * When ::kmausers is invoked with the -f flag, we print out the information
39810Sstevel@tonic-gate  * for each bufctl as well as updating the hash table.
39820Sstevel@tonic-gate  */
39830Sstevel@tonic-gate static int
39840Sstevel@tonic-gate kmause2(uintptr_t addr, const kmem_bufctl_audit_t *bcp, kmusers_t *kmu)
39850Sstevel@tonic-gate {
39860Sstevel@tonic-gate 	int i, depth = MIN(bcp->bc_depth, KMEM_STACK_DEPTH);
39870Sstevel@tonic-gate 	const kmem_cache_t *cp = kmu->kmu_cache;
39880Sstevel@tonic-gate 	kmem_bufctl_t bufctl;
39890Sstevel@tonic-gate 
39900Sstevel@tonic-gate 	if (kmu->kmu_addr) {
39910Sstevel@tonic-gate 		if (mdb_vread(&bufctl, sizeof (bufctl),  addr) == -1)
39920Sstevel@tonic-gate 			mdb_warn("couldn't read bufctl at %p", addr);
39930Sstevel@tonic-gate 		else if (kmu->kmu_addr < (uintptr_t)bufctl.bc_addr ||
39940Sstevel@tonic-gate 		    kmu->kmu_addr >= (uintptr_t)bufctl.bc_addr +
39950Sstevel@tonic-gate 		    cp->cache_bufsize)
39960Sstevel@tonic-gate 			return (WALK_NEXT);
39970Sstevel@tonic-gate 	}
39980Sstevel@tonic-gate 
39990Sstevel@tonic-gate 	mdb_printf("size %d, addr %p, thread %p, cache %s\n",
40000Sstevel@tonic-gate 	    cp->cache_bufsize, addr, bcp->bc_thread, cp->cache_name);
40010Sstevel@tonic-gate 
40020Sstevel@tonic-gate 	for (i = 0; i < depth; i++)
40030Sstevel@tonic-gate 		mdb_printf("\t %a\n", bcp->bc_stack[i]);
40040Sstevel@tonic-gate 
40050Sstevel@tonic-gate 	kmu_add(kmu, bcp, cp->cache_bufsize, cp->cache_bufsize);
40060Sstevel@tonic-gate 	return (WALK_NEXT);
40070Sstevel@tonic-gate }
40080Sstevel@tonic-gate 
40090Sstevel@tonic-gate /*
40100Sstevel@tonic-gate  * We sort our results by allocation size before printing them.
40110Sstevel@tonic-gate  */
40120Sstevel@tonic-gate static int
40130Sstevel@tonic-gate kmownercmp(const void *lp, const void *rp)
40140Sstevel@tonic-gate {
40150Sstevel@tonic-gate 	const kmowner_t *lhs = lp;
40160Sstevel@tonic-gate 	const kmowner_t *rhs = rp;
40170Sstevel@tonic-gate 
40180Sstevel@tonic-gate 	return (rhs->kmo_total_size - lhs->kmo_total_size);
40190Sstevel@tonic-gate }
40200Sstevel@tonic-gate 
40210Sstevel@tonic-gate /*
40220Sstevel@tonic-gate  * The main engine of ::kmausers is relatively straightforward: First we
40230Sstevel@tonic-gate  * accumulate our list of kmem_cache_t addresses into the kmclist_t. Next we
40240Sstevel@tonic-gate  * iterate over the allocated bufctls of each cache in the list.  Finally,
40250Sstevel@tonic-gate  * we sort and print our results.
40260Sstevel@tonic-gate  */
40270Sstevel@tonic-gate /*ARGSUSED*/
40280Sstevel@tonic-gate int
40290Sstevel@tonic-gate kmausers(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
40300Sstevel@tonic-gate {
40310Sstevel@tonic-gate 	int mem_threshold = 8192;	/* Minimum # bytes for printing */
40320Sstevel@tonic-gate 	int cnt_threshold = 100;	/* Minimum # blocks for printing */
40330Sstevel@tonic-gate 	int audited_caches = 0;		/* Number of KMF_AUDIT caches found */
40340Sstevel@tonic-gate 	int do_all_caches = 1;		/* Do all caches (no arguments) */
40350Sstevel@tonic-gate 	int opt_e = FALSE;		/* Include "small" users */
40360Sstevel@tonic-gate 	int opt_f = FALSE;		/* Print stack traces */
40370Sstevel@tonic-gate 
40380Sstevel@tonic-gate 	mdb_walk_cb_t callback = (mdb_walk_cb_t)kmause1;
40390Sstevel@tonic-gate 	kmowner_t *kmo, *kmoend;
40400Sstevel@tonic-gate 	int i, oelems;
40410Sstevel@tonic-gate 
40420Sstevel@tonic-gate 	kmclist_t kmc;
40430Sstevel@tonic-gate 	kmusers_t kmu;
40440Sstevel@tonic-gate 
40450Sstevel@tonic-gate 	bzero(&kmc, sizeof (kmc));
40460Sstevel@tonic-gate 	bzero(&kmu, sizeof (kmu));
40470Sstevel@tonic-gate 
40480Sstevel@tonic-gate 	while ((i = mdb_getopts(argc, argv,
40490Sstevel@tonic-gate 	    'e', MDB_OPT_SETBITS, TRUE, &opt_e,
40500Sstevel@tonic-gate 	    'f', MDB_OPT_SETBITS, TRUE, &opt_f, NULL)) != argc) {
40510Sstevel@tonic-gate 
40520Sstevel@tonic-gate 		argv += i;	/* skip past options we just processed */
40530Sstevel@tonic-gate 		argc -= i;	/* adjust argc */
40540Sstevel@tonic-gate 
40550Sstevel@tonic-gate 		if (argv->a_type != MDB_TYPE_STRING || *argv->a_un.a_str == '-')
40560Sstevel@tonic-gate 			return (DCMD_USAGE);
40570Sstevel@tonic-gate 
40580Sstevel@tonic-gate 		oelems = kmc.kmc_nelems;
40590Sstevel@tonic-gate 		kmc.kmc_name = argv->a_un.a_str;
40600Sstevel@tonic-gate 		(void) mdb_walk("kmem_cache", (mdb_walk_cb_t)kmc_add, &kmc);
40610Sstevel@tonic-gate 
40620Sstevel@tonic-gate 		if (kmc.kmc_nelems == oelems) {
40630Sstevel@tonic-gate 			mdb_warn("unknown kmem cache: %s\n", kmc.kmc_name);
40640Sstevel@tonic-gate 			return (DCMD_ERR);
40650Sstevel@tonic-gate 		}
40660Sstevel@tonic-gate 
40670Sstevel@tonic-gate 		do_all_caches = 0;
40680Sstevel@tonic-gate 		argv++;
40690Sstevel@tonic-gate 		argc--;
40700Sstevel@tonic-gate 	}
40710Sstevel@tonic-gate 
40720Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC) {
40730Sstevel@tonic-gate 		opt_f = TRUE;
40740Sstevel@tonic-gate 		kmu.kmu_addr = addr;
40750Sstevel@tonic-gate 	} else {
40760Sstevel@tonic-gate 		kmu.kmu_addr = NULL;
40770Sstevel@tonic-gate 	}
40780Sstevel@tonic-gate 
40790Sstevel@tonic-gate 	if (opt_e)
40800Sstevel@tonic-gate 		mem_threshold = cnt_threshold = 0;
40810Sstevel@tonic-gate 
40820Sstevel@tonic-gate 	if (opt_f)
40830Sstevel@tonic-gate 		callback = (mdb_walk_cb_t)kmause2;
40840Sstevel@tonic-gate 
40850Sstevel@tonic-gate 	if (do_all_caches) {
40860Sstevel@tonic-gate 		kmc.kmc_name = NULL; /* match all cache names */
40870Sstevel@tonic-gate 		(void) mdb_walk("kmem_cache", (mdb_walk_cb_t)kmc_add, &kmc);
40880Sstevel@tonic-gate 	}
40890Sstevel@tonic-gate 
40900Sstevel@tonic-gate 	for (i = 0; i < kmc.kmc_nelems; i++) {
40910Sstevel@tonic-gate 		uintptr_t cp = kmc.kmc_caches[i];
40920Sstevel@tonic-gate 		kmem_cache_t c;
40930Sstevel@tonic-gate 
40940Sstevel@tonic-gate 		if (mdb_vread(&c, sizeof (c), cp) == -1) {
40950Sstevel@tonic-gate 			mdb_warn("failed to read cache at %p", cp);
40960Sstevel@tonic-gate 			continue;
40970Sstevel@tonic-gate 		}
40980Sstevel@tonic-gate 
40990Sstevel@tonic-gate 		if (!(c.cache_flags & KMF_AUDIT)) {
41000Sstevel@tonic-gate 			if (!do_all_caches) {
41010Sstevel@tonic-gate 				mdb_warn("KMF_AUDIT is not enabled for %s\n",
41020Sstevel@tonic-gate 				    c.cache_name);
41030Sstevel@tonic-gate 			}
41040Sstevel@tonic-gate 			continue;
41050Sstevel@tonic-gate 		}
41060Sstevel@tonic-gate 
41070Sstevel@tonic-gate 		kmu.kmu_cache = &c;
41080Sstevel@tonic-gate 		(void) mdb_pwalk("bufctl", callback, &kmu, cp);
41090Sstevel@tonic-gate 		audited_caches++;
41100Sstevel@tonic-gate 	}
41110Sstevel@tonic-gate 
41120Sstevel@tonic-gate 	if (audited_caches == 0 && do_all_caches) {
41130Sstevel@tonic-gate 		mdb_warn("KMF_AUDIT is not enabled for any caches\n");
41140Sstevel@tonic-gate 		return (DCMD_ERR);
41150Sstevel@tonic-gate 	}
41160Sstevel@tonic-gate 
41170Sstevel@tonic-gate 	qsort(kmu.kmu_hash, kmu.kmu_nelems, sizeof (kmowner_t), kmownercmp);
41180Sstevel@tonic-gate 	kmoend = kmu.kmu_hash + kmu.kmu_nelems;
41190Sstevel@tonic-gate 
41200Sstevel@tonic-gate 	for (kmo = kmu.kmu_hash; kmo < kmoend; kmo++) {
41210Sstevel@tonic-gate 		if (kmo->kmo_total_size < mem_threshold &&
41220Sstevel@tonic-gate 		    kmo->kmo_num < cnt_threshold)
41230Sstevel@tonic-gate 			continue;
41240Sstevel@tonic-gate 		mdb_printf("%lu bytes for %u allocations with data size %lu:\n",
41250Sstevel@tonic-gate 		    kmo->kmo_total_size, kmo->kmo_num, kmo->kmo_data_size);
41260Sstevel@tonic-gate 		for (i = 0; i < kmo->kmo_depth; i++)
41270Sstevel@tonic-gate 			mdb_printf("\t %a\n", kmo->kmo_stack[i]);
41280Sstevel@tonic-gate 	}
41290Sstevel@tonic-gate 
41300Sstevel@tonic-gate 	return (DCMD_OK);
41310Sstevel@tonic-gate }
41320Sstevel@tonic-gate 
41330Sstevel@tonic-gate void
41340Sstevel@tonic-gate kmausers_help(void)
41350Sstevel@tonic-gate {
41360Sstevel@tonic-gate 	mdb_printf(
41370Sstevel@tonic-gate 	    "Displays the largest users of the kmem allocator, sorted by \n"
41380Sstevel@tonic-gate 	    "trace.  If one or more caches is specified, only those caches\n"
41390Sstevel@tonic-gate 	    "will be searched.  By default, all caches are searched.  If an\n"
41400Sstevel@tonic-gate 	    "address is specified, then only those allocations which include\n"
41410Sstevel@tonic-gate 	    "the given address are displayed.  Specifying an address implies\n"
41420Sstevel@tonic-gate 	    "-f.\n"
41430Sstevel@tonic-gate 	    "\n"
41440Sstevel@tonic-gate 	    "\t-e\tInclude all users, not just the largest\n"
41450Sstevel@tonic-gate 	    "\t-f\tDisplay individual allocations.  By default, users are\n"
41460Sstevel@tonic-gate 	    "\t\tgrouped by stack\n");
41470Sstevel@tonic-gate }
41480Sstevel@tonic-gate 
41490Sstevel@tonic-gate static int
41500Sstevel@tonic-gate kmem_ready_check(void)
41510Sstevel@tonic-gate {
41520Sstevel@tonic-gate 	int ready;
41530Sstevel@tonic-gate 
41540Sstevel@tonic-gate 	if (mdb_readvar(&ready, "kmem_ready") < 0)
41550Sstevel@tonic-gate 		return (-1); /* errno is set for us */
41560Sstevel@tonic-gate 
41570Sstevel@tonic-gate 	return (ready);
41580Sstevel@tonic-gate }
41590Sstevel@tonic-gate 
41600Sstevel@tonic-gate /*ARGSUSED*/
41610Sstevel@tonic-gate static void
41621528Sjwadams kmem_statechange_cb(void *arg)
41630Sstevel@tonic-gate {
41641528Sjwadams 	static int been_ready = 0;
41651528Sjwadams 
41661528Sjwadams 	leaky_cleanup(1);	/* state changes invalidate leaky state */
41671528Sjwadams 
41681528Sjwadams 	if (been_ready)
41691528Sjwadams 		return;
41701528Sjwadams 
41710Sstevel@tonic-gate 	if (kmem_ready_check() <= 0)
41720Sstevel@tonic-gate 		return;
41730Sstevel@tonic-gate 
41741528Sjwadams 	been_ready = 1;
41750Sstevel@tonic-gate 	(void) mdb_walk("kmem_cache", (mdb_walk_cb_t)kmem_init_walkers, NULL);
41760Sstevel@tonic-gate }
41770Sstevel@tonic-gate 
41780Sstevel@tonic-gate void
41790Sstevel@tonic-gate kmem_init(void)
41800Sstevel@tonic-gate {
41810Sstevel@tonic-gate 	mdb_walker_t w = {
41820Sstevel@tonic-gate 		"kmem_cache", "walk list of kmem caches", kmem_cache_walk_init,
41830Sstevel@tonic-gate 		kmem_cache_walk_step, kmem_cache_walk_fini
41840Sstevel@tonic-gate 	};
41850Sstevel@tonic-gate 
41860Sstevel@tonic-gate 	/*
41870Sstevel@tonic-gate 	 * If kmem is ready, we'll need to invoke the kmem_cache walker
41880Sstevel@tonic-gate 	 * immediately.  Walkers in the linkage structure won't be ready until
41890Sstevel@tonic-gate 	 * _mdb_init returns, so we'll need to add this one manually.  If kmem
41900Sstevel@tonic-gate 	 * is ready, we'll use the walker to initialize the caches.  If kmem
41910Sstevel@tonic-gate 	 * isn't ready, we'll register a callback that will allow us to defer
41920Sstevel@tonic-gate 	 * cache walking until it is.
41930Sstevel@tonic-gate 	 */
41940Sstevel@tonic-gate 	if (mdb_add_walker(&w) != 0) {
41950Sstevel@tonic-gate 		mdb_warn("failed to add kmem_cache walker");
41960Sstevel@tonic-gate 		return;
41970Sstevel@tonic-gate 	}
41980Sstevel@tonic-gate 
41991528Sjwadams 	(void) mdb_callback_add(MDB_CALLBACK_STCHG, kmem_statechange_cb, NULL);
42001528Sjwadams 	kmem_statechange_cb(NULL);
42010Sstevel@tonic-gate }
42020Sstevel@tonic-gate 
42030Sstevel@tonic-gate typedef struct whatthread {
42040Sstevel@tonic-gate 	uintptr_t	wt_target;
42050Sstevel@tonic-gate 	int		wt_verbose;
42060Sstevel@tonic-gate } whatthread_t;
42070Sstevel@tonic-gate 
42080Sstevel@tonic-gate static int
42090Sstevel@tonic-gate whatthread_walk_thread(uintptr_t addr, const kthread_t *t, whatthread_t *w)
42100Sstevel@tonic-gate {
42110Sstevel@tonic-gate 	uintptr_t current, data;
42120Sstevel@tonic-gate 
42130Sstevel@tonic-gate 	if (t->t_stkbase == NULL)
42140Sstevel@tonic-gate 		return (WALK_NEXT);
42150Sstevel@tonic-gate 
42160Sstevel@tonic-gate 	/*
42170Sstevel@tonic-gate 	 * Warn about swapped out threads, but drive on anyway
42180Sstevel@tonic-gate 	 */
42190Sstevel@tonic-gate 	if (!(t->t_schedflag & TS_LOAD)) {
42200Sstevel@tonic-gate 		mdb_warn("thread %p's stack swapped out\n", addr);
42210Sstevel@tonic-gate 		return (WALK_NEXT);
42220Sstevel@tonic-gate 	}
42230Sstevel@tonic-gate 
42240Sstevel@tonic-gate 	/*
42250Sstevel@tonic-gate 	 * Search the thread's stack for the given pointer.  Note that it would
42260Sstevel@tonic-gate 	 * be more efficient to follow ::kgrep's lead and read in page-sized
42270Sstevel@tonic-gate 	 * chunks, but this routine is already fast and simple.
42280Sstevel@tonic-gate 	 */
42290Sstevel@tonic-gate 	for (current = (uintptr_t)t->t_stkbase; current < (uintptr_t)t->t_stk;
42300Sstevel@tonic-gate 	    current += sizeof (uintptr_t)) {
42310Sstevel@tonic-gate 		if (mdb_vread(&data, sizeof (data), current) == -1) {
42320Sstevel@tonic-gate 			mdb_warn("couldn't read thread %p's stack at %p",
42330Sstevel@tonic-gate 			    addr, current);
42340Sstevel@tonic-gate 			return (WALK_ERR);
42350Sstevel@tonic-gate 		}
42360Sstevel@tonic-gate 
42370Sstevel@tonic-gate 		if (data == w->wt_target) {
42380Sstevel@tonic-gate 			if (w->wt_verbose) {
42390Sstevel@tonic-gate 				mdb_printf("%p in thread %p's stack%s\n",
42400Sstevel@tonic-gate 				    current, addr, stack_active(t, current));
42410Sstevel@tonic-gate 			} else {
42420Sstevel@tonic-gate 				mdb_printf("%#lr\n", addr);
42430Sstevel@tonic-gate 				return (WALK_NEXT);
42440Sstevel@tonic-gate 			}
42450Sstevel@tonic-gate 		}
42460Sstevel@tonic-gate 	}
42470Sstevel@tonic-gate 
42480Sstevel@tonic-gate 	return (WALK_NEXT);
42490Sstevel@tonic-gate }
42500Sstevel@tonic-gate 
42510Sstevel@tonic-gate int
42520Sstevel@tonic-gate whatthread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
42530Sstevel@tonic-gate {
42540Sstevel@tonic-gate 	whatthread_t w;
42550Sstevel@tonic-gate 
42560Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
42570Sstevel@tonic-gate 		return (DCMD_USAGE);
42580Sstevel@tonic-gate 
42590Sstevel@tonic-gate 	w.wt_verbose = FALSE;
42600Sstevel@tonic-gate 	w.wt_target = addr;
42610Sstevel@tonic-gate 
42620Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
42630Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &w.wt_verbose, NULL) != argc)
42640Sstevel@tonic-gate 		return (DCMD_USAGE);
42650Sstevel@tonic-gate 
42660Sstevel@tonic-gate 	if (mdb_walk("thread", (mdb_walk_cb_t)whatthread_walk_thread, &w)
42670Sstevel@tonic-gate 	    == -1) {
42680Sstevel@tonic-gate 		mdb_warn("couldn't walk threads");
42690Sstevel@tonic-gate 		return (DCMD_ERR);
42700Sstevel@tonic-gate 	}
42710Sstevel@tonic-gate 
42720Sstevel@tonic-gate 	return (DCMD_OK);
42730Sstevel@tonic-gate }
4274