xref: /titanic_44/usr/src/cmd/mdb/common/modules/genunix/leaky.c (revision 789d94c2889bedf502063bc22addcabfa798a438)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*789d94c2Sjwadams  * Common Development and Distribution License (the "License").
6*789d94c2Sjwadams  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*789d94c2Sjwadams  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * A generic memory leak detector.  The target interface, defined in
307c478bd9Sstevel@tonic-gate  * <leaky_impl.h>, is implemented by the genunix and libumem dmods to fill
317c478bd9Sstevel@tonic-gate  * in the details of operation.
327c478bd9Sstevel@tonic-gate  */
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate #include "leaky.h"
377c478bd9Sstevel@tonic-gate #include "leaky_impl.h"
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #define	LK_BUFCTLHSIZE	127
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate /*
427c478bd9Sstevel@tonic-gate  * We re-use the low bit of the lkm_addr as the 'marked' bit.
437c478bd9Sstevel@tonic-gate  */
447c478bd9Sstevel@tonic-gate #define	LK_MARKED(b)	((uintptr_t)(b) & 1)
457c478bd9Sstevel@tonic-gate #define	LK_MARK(b)	((b) |= 1)
467c478bd9Sstevel@tonic-gate #define	LK_ADDR(b)	((uintptr_t)(b) & ~1UL)
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate /*
497c478bd9Sstevel@tonic-gate  * Possible values for lk_state.
507c478bd9Sstevel@tonic-gate  */
517c478bd9Sstevel@tonic-gate #define	LK_CLEAN	0	/* No outstanding mdb_alloc()'s */
527c478bd9Sstevel@tonic-gate #define	LK_SWEEPING	1	/* Potentially some outstanding mdb_alloc()'s */
537c478bd9Sstevel@tonic-gate #define	LK_DONE		2	/* All mdb_alloc()'s complete */
547c478bd9Sstevel@tonic-gate #define	LK_CLEANING	3	/* Currently cleaning prior mdb_alloc()'s */
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate static volatile int lk_state;
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate #define	LK_STATE_SIZE	10000	/* completely arbitrary */
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate typedef int leak_ndx_t;		/* change if >2 billion buffers are needed */
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate typedef struct leak_state {
637c478bd9Sstevel@tonic-gate 	struct leak_state *lks_next;
647c478bd9Sstevel@tonic-gate 	leak_ndx_t lks_stack[LK_STATE_SIZE];
657c478bd9Sstevel@tonic-gate } leak_state_t;
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate typedef struct leak_beans {
687c478bd9Sstevel@tonic-gate 	int lkb_dups;
697c478bd9Sstevel@tonic-gate 	int lkb_follows;
707c478bd9Sstevel@tonic-gate 	int lkb_misses;
717c478bd9Sstevel@tonic-gate 	int lkb_dismissals;
727c478bd9Sstevel@tonic-gate 	int lkb_pushes;
737c478bd9Sstevel@tonic-gate 	int lkb_deepest;
747c478bd9Sstevel@tonic-gate } leak_beans_t;
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate typedef struct leak_type {
777c478bd9Sstevel@tonic-gate 	int		lt_type;
787c478bd9Sstevel@tonic-gate 	size_t		lt_leaks;
797c478bd9Sstevel@tonic-gate 	leak_bufctl_t	**lt_sorted;
807c478bd9Sstevel@tonic-gate } leak_type_t;
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate typedef struct leak_walk {
837c478bd9Sstevel@tonic-gate 	int lkw_ndx;
847c478bd9Sstevel@tonic-gate 	leak_bufctl_t *lkw_current;
857c478bd9Sstevel@tonic-gate 	leak_bufctl_t *lkw_hash_next;
867c478bd9Sstevel@tonic-gate } leak_walk_t;
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate #define	LK_SCAN_BUFFER_SIZE	16384
897c478bd9Sstevel@tonic-gate static uintptr_t *lk_scan_buffer;
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate static leak_mtab_t *lk_mtab;
927c478bd9Sstevel@tonic-gate static leak_state_t *lk_free_state;
937c478bd9Sstevel@tonic-gate static leak_ndx_t lk_nbuffers;
947c478bd9Sstevel@tonic-gate static leak_beans_t lk_beans;
957c478bd9Sstevel@tonic-gate static leak_bufctl_t *lk_bufctl[LK_BUFCTLHSIZE];
967c478bd9Sstevel@tonic-gate static leak_type_t lk_types[LK_NUM_TYPES];
977c478bd9Sstevel@tonic-gate static size_t lk_memusage;
987c478bd9Sstevel@tonic-gate #ifndef _KMDB
997c478bd9Sstevel@tonic-gate static hrtime_t lk_begin;
1007c478bd9Sstevel@tonic-gate static hrtime_t lk_vbegin;
1017c478bd9Sstevel@tonic-gate #endif
1027c478bd9Sstevel@tonic-gate static uint_t lk_verbose = FALSE;
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate static void
leaky_verbose(char * str,uint64_t stat)1057c478bd9Sstevel@tonic-gate leaky_verbose(char *str, uint64_t stat)
1067c478bd9Sstevel@tonic-gate {
1077c478bd9Sstevel@tonic-gate 	if (lk_verbose == FALSE)
1087c478bd9Sstevel@tonic-gate 		return;
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	mdb_printf("findleaks: ");
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	if (str == NULL) {
1137c478bd9Sstevel@tonic-gate 		mdb_printf("\n");
1147c478bd9Sstevel@tonic-gate 		return;
1157c478bd9Sstevel@tonic-gate 	}
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate 	mdb_printf("%*s => %lld\n", 30, str, stat);
1187c478bd9Sstevel@tonic-gate }
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate static void
leaky_verbose_perc(char * str,uint64_t stat,uint64_t total)1217c478bd9Sstevel@tonic-gate leaky_verbose_perc(char *str, uint64_t stat, uint64_t total)
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate 	uint_t perc = (stat * 100) / total;
1247c478bd9Sstevel@tonic-gate 	uint_t tenths = ((stat * 1000) / total) % 10;
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 	if (lk_verbose == FALSE)
1277c478bd9Sstevel@tonic-gate 		return;
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	mdb_printf("findleaks: %*s => %-13lld (%2d.%1d%%)\n",
1307c478bd9Sstevel@tonic-gate 	    30, str, stat, perc, tenths);
1317c478bd9Sstevel@tonic-gate }
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate static void
leaky_verbose_begin(void)1347c478bd9Sstevel@tonic-gate leaky_verbose_begin(void)
1357c478bd9Sstevel@tonic-gate {
1367c478bd9Sstevel@tonic-gate 	/* kmdb can't tell time */
1377c478bd9Sstevel@tonic-gate #ifndef _KMDB
1387c478bd9Sstevel@tonic-gate 	extern hrtime_t gethrvtime(void);
1397c478bd9Sstevel@tonic-gate 	lk_begin = gethrtime();
1407c478bd9Sstevel@tonic-gate 	lk_vbegin = gethrvtime();
1417c478bd9Sstevel@tonic-gate #endif
1427c478bd9Sstevel@tonic-gate 	lk_memusage = 0;
1437c478bd9Sstevel@tonic-gate }
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate static void
leaky_verbose_end(void)1467c478bd9Sstevel@tonic-gate leaky_verbose_end(void)
1477c478bd9Sstevel@tonic-gate {
1487c478bd9Sstevel@tonic-gate 	/* kmdb can't tell time */
1497c478bd9Sstevel@tonic-gate #ifndef _KMDB
1507c478bd9Sstevel@tonic-gate 	extern hrtime_t gethrvtime(void);
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	hrtime_t ts = gethrtime() - lk_begin;
1537c478bd9Sstevel@tonic-gate 	hrtime_t sec = ts / (hrtime_t)NANOSEC;
1547c478bd9Sstevel@tonic-gate 	hrtime_t nsec = ts % (hrtime_t)NANOSEC;
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	hrtime_t vts = gethrvtime() - lk_vbegin;
1577c478bd9Sstevel@tonic-gate 	hrtime_t vsec = vts / (hrtime_t)NANOSEC;
1587c478bd9Sstevel@tonic-gate 	hrtime_t vnsec = vts % (hrtime_t)NANOSEC;
1597c478bd9Sstevel@tonic-gate #endif
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	if (lk_verbose == FALSE)
1627c478bd9Sstevel@tonic-gate 		return;
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	mdb_printf("findleaks: %*s => %lu kB\n",
1657c478bd9Sstevel@tonic-gate 	    30, "peak memory usage", (lk_memusage + 1023)/1024);
1667c478bd9Sstevel@tonic-gate #ifndef _KMDB
1677c478bd9Sstevel@tonic-gate 	mdb_printf("findleaks: %*s => %lld.%lld seconds\n",
1687c478bd9Sstevel@tonic-gate 	    30, "elapsed CPU time", vsec, (vnsec * 10)/(hrtime_t)NANOSEC);
1697c478bd9Sstevel@tonic-gate 	mdb_printf("findleaks: %*s => %lld.%lld seconds\n",
1707c478bd9Sstevel@tonic-gate 	    30, "elapsed wall time", sec, (nsec * 10)/(hrtime_t)NANOSEC);
1717c478bd9Sstevel@tonic-gate #endif
1727c478bd9Sstevel@tonic-gate 	leaky_verbose(NULL, 0);
1737c478bd9Sstevel@tonic-gate }
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate static void *
leaky_alloc(size_t sz,uint_t flags)1767c478bd9Sstevel@tonic-gate leaky_alloc(size_t sz, uint_t flags)
1777c478bd9Sstevel@tonic-gate {
1787c478bd9Sstevel@tonic-gate 	void *buf = mdb_alloc(sz, flags);
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	if (buf != NULL)
1817c478bd9Sstevel@tonic-gate 		lk_memusage += sz;
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	return (buf);
1847c478bd9Sstevel@tonic-gate }
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate static void *
leaky_zalloc(size_t sz,uint_t flags)1877c478bd9Sstevel@tonic-gate leaky_zalloc(size_t sz, uint_t flags)
1887c478bd9Sstevel@tonic-gate {
1897c478bd9Sstevel@tonic-gate 	void *buf = mdb_zalloc(sz, flags);
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	if (buf != NULL)
1927c478bd9Sstevel@tonic-gate 		lk_memusage += sz;
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 	return (buf);
1957c478bd9Sstevel@tonic-gate }
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate static int
leaky_mtabcmp(const void * l,const void * r)1987c478bd9Sstevel@tonic-gate leaky_mtabcmp(const void *l, const void *r)
1997c478bd9Sstevel@tonic-gate {
2007c478bd9Sstevel@tonic-gate 	const leak_mtab_t *lhs = (const leak_mtab_t *)l;
2017c478bd9Sstevel@tonic-gate 	const leak_mtab_t *rhs = (const leak_mtab_t *)r;
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	if (lhs->lkm_base < rhs->lkm_base)
2047c478bd9Sstevel@tonic-gate 		return (-1);
2057c478bd9Sstevel@tonic-gate 	if (lhs->lkm_base > rhs->lkm_base)
2067c478bd9Sstevel@tonic-gate 		return (1);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	return (0);
2097c478bd9Sstevel@tonic-gate }
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate static leak_ndx_t
leaky_search(uintptr_t addr)2127c478bd9Sstevel@tonic-gate leaky_search(uintptr_t addr)
2137c478bd9Sstevel@tonic-gate {
2147c478bd9Sstevel@tonic-gate 	leak_ndx_t left = 0, right = lk_nbuffers - 1, guess;
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	while (right >= left) {
2177c478bd9Sstevel@tonic-gate 		guess = (right + left) >> 1;
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 		if (addr < LK_ADDR(lk_mtab[guess].lkm_base)) {
2207c478bd9Sstevel@tonic-gate 			right = guess - 1;
2217c478bd9Sstevel@tonic-gate 			continue;
2227c478bd9Sstevel@tonic-gate 		}
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 		if (addr >= lk_mtab[guess].lkm_limit) {
2257c478bd9Sstevel@tonic-gate 			left = guess + 1;
2267c478bd9Sstevel@tonic-gate 			continue;
2277c478bd9Sstevel@tonic-gate 		}
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 		return (guess);
2307c478bd9Sstevel@tonic-gate 	}
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 	return (-1);
2337c478bd9Sstevel@tonic-gate }
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate void
leaky_grep(uintptr_t addr,size_t size)2367c478bd9Sstevel@tonic-gate leaky_grep(uintptr_t addr, size_t size)
2377c478bd9Sstevel@tonic-gate {
2387c478bd9Sstevel@tonic-gate 	uintptr_t *buf, *cur, *end;
2397c478bd9Sstevel@tonic-gate 	size_t bytes, newsz, nptrs;
2407c478bd9Sstevel@tonic-gate 	leak_state_t *state = NULL, *new_state;
2417c478bd9Sstevel@tonic-gate 	uint_t state_idx;
2427c478bd9Sstevel@tonic-gate 	uintptr_t min = LK_ADDR(lk_mtab[0].lkm_base);
2437c478bd9Sstevel@tonic-gate 	uintptr_t max = lk_mtab[lk_nbuffers - 1].lkm_limit;
2447c478bd9Sstevel@tonic-gate 	int dups = 0, misses = 0, depth = 0, deepest = 0;
2457c478bd9Sstevel@tonic-gate 	int follows = 0, dismissals = 0, pushes = 0;
2467c478bd9Sstevel@tonic-gate 	leak_ndx_t mtab_ndx;
2477c478bd9Sstevel@tonic-gate 	leak_mtab_t *lmp;
2487c478bd9Sstevel@tonic-gate 	uintptr_t nbase;
2497c478bd9Sstevel@tonic-gate 	uintptr_t base;
2507c478bd9Sstevel@tonic-gate 	size_t base_size;
2517c478bd9Sstevel@tonic-gate 	const uintptr_t mask = sizeof (uintptr_t) - 1;
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 	if (addr == NULL || size == 0)
2547c478bd9Sstevel@tonic-gate 		return;
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 	state_idx = 0;
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	/*
2597c478bd9Sstevel@tonic-gate 	 * Our main loop, led by the 'pop' label:
2607c478bd9Sstevel@tonic-gate 	 *	1)  read in a buffer piece by piece,
2617c478bd9Sstevel@tonic-gate 	 *	2)  mark all unmarked mtab entries reachable from it, and
2627c478bd9Sstevel@tonic-gate 	 *	    either scan them in-line or push them onto our stack of
2637c478bd9Sstevel@tonic-gate 	 *	    unfinished work.
2647c478bd9Sstevel@tonic-gate 	 *	3)  pop the top mtab entry off the stack, and loop.
2657c478bd9Sstevel@tonic-gate 	 */
2667c478bd9Sstevel@tonic-gate pop:
2677c478bd9Sstevel@tonic-gate 	base = addr;
2687c478bd9Sstevel@tonic-gate 	base_size = size;
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 	/*
2717c478bd9Sstevel@tonic-gate 	 * If our address isn't pointer-aligned, we need to align it and
2727c478bd9Sstevel@tonic-gate 	 * whack the size appropriately.
2737c478bd9Sstevel@tonic-gate 	 */
2747c478bd9Sstevel@tonic-gate 	if (size < mask) {
2757c478bd9Sstevel@tonic-gate 		size = 0;
2767c478bd9Sstevel@tonic-gate 	} else if (addr & mask) {
2777c478bd9Sstevel@tonic-gate 		size -= (mask + 1) - (addr & mask);
2787c478bd9Sstevel@tonic-gate 		addr += (mask + 1) - (addr & mask);
2797c478bd9Sstevel@tonic-gate 	}
2807c478bd9Sstevel@tonic-gate 	size -= (size & mask);
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	while (size > 0) {
2837c478bd9Sstevel@tonic-gate 		buf = lk_scan_buffer;
2847c478bd9Sstevel@tonic-gate 		end = &buf[LK_SCAN_BUFFER_SIZE / sizeof (uintptr_t)];
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 		bytes = MIN(size, LK_SCAN_BUFFER_SIZE);
2877c478bd9Sstevel@tonic-gate 		cur = end - (bytes / sizeof (uintptr_t));
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 		if (mdb_vread(cur, bytes, addr) == -1) {
2907c478bd9Sstevel@tonic-gate 			mdb_warn("[%p, %p): couldn't read %ld bytes at %p",
2917c478bd9Sstevel@tonic-gate 			    base, base + base_size, bytes, addr);
2927c478bd9Sstevel@tonic-gate 			break;
2937c478bd9Sstevel@tonic-gate 		}
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 		addr += bytes;
2967c478bd9Sstevel@tonic-gate 		size -= bytes;
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 		/*
2997c478bd9Sstevel@tonic-gate 		 * The buffer looks like:  ('+'s are unscanned data)
3007c478bd9Sstevel@tonic-gate 		 *
3017c478bd9Sstevel@tonic-gate 		 * -----------------------------++++++++++++++++
3027c478bd9Sstevel@tonic-gate 		 * |				|		|
3037c478bd9Sstevel@tonic-gate 		 * buf				cur		end
3047c478bd9Sstevel@tonic-gate 		 *
3057c478bd9Sstevel@tonic-gate 		 * cur scans forward.  When we encounter a new buffer, and
3067c478bd9Sstevel@tonic-gate 		 * it will fit behind "cur", we read it in and back up cur,
3077c478bd9Sstevel@tonic-gate 		 * processing it immediately.
3087c478bd9Sstevel@tonic-gate 		 */
3097c478bd9Sstevel@tonic-gate 		while (cur < end) {
3107c478bd9Sstevel@tonic-gate 			uintptr_t ptr = *cur++;
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 			if (ptr < min || ptr > max) {
3137c478bd9Sstevel@tonic-gate 				dismissals++;
3147c478bd9Sstevel@tonic-gate 				continue;
3157c478bd9Sstevel@tonic-gate 			}
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 			if ((mtab_ndx = leaky_search(ptr)) == -1) {
3187c478bd9Sstevel@tonic-gate 				misses++;
3197c478bd9Sstevel@tonic-gate 				continue;
3207c478bd9Sstevel@tonic-gate 			}
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 			lmp = &lk_mtab[mtab_ndx];
3237c478bd9Sstevel@tonic-gate 			if (LK_MARKED(lmp->lkm_base)) {
3247c478bd9Sstevel@tonic-gate 				dups++;			/* already seen */
3257c478bd9Sstevel@tonic-gate 				continue;
3267c478bd9Sstevel@tonic-gate 			}
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 			/*
3297c478bd9Sstevel@tonic-gate 			 * Found an unmarked buffer.  Mark it, then either
3307c478bd9Sstevel@tonic-gate 			 * read it in, or add it to the stack of pending work.
3317c478bd9Sstevel@tonic-gate 			 */
3327c478bd9Sstevel@tonic-gate 			follows++;
3337c478bd9Sstevel@tonic-gate 			LK_MARK(lmp->lkm_base);
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 			nbase = LK_ADDR(lmp->lkm_base);
3367c478bd9Sstevel@tonic-gate 			newsz = lmp->lkm_limit - nbase;
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 			nptrs = newsz / sizeof (uintptr_t);
3397c478bd9Sstevel@tonic-gate 			newsz = nptrs * sizeof (uintptr_t);
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 			if ((nbase & mask) == 0 && nptrs <= (cur - buf) &&
3427c478bd9Sstevel@tonic-gate 			    mdb_vread(cur - nptrs, newsz, nbase) != -1) {
3437c478bd9Sstevel@tonic-gate 				cur -= nptrs;
3447c478bd9Sstevel@tonic-gate 				continue;
3457c478bd9Sstevel@tonic-gate 			}
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 			/*
3487c478bd9Sstevel@tonic-gate 			 * couldn't process it in-place -- add it to the
3497c478bd9Sstevel@tonic-gate 			 * stack.
3507c478bd9Sstevel@tonic-gate 			 */
3517c478bd9Sstevel@tonic-gate 			if (state == NULL || state_idx == LK_STATE_SIZE) {
3527c478bd9Sstevel@tonic-gate 				if ((new_state = lk_free_state) != NULL)
3537c478bd9Sstevel@tonic-gate 					lk_free_state = new_state->lks_next;
3547c478bd9Sstevel@tonic-gate 				else
3557c478bd9Sstevel@tonic-gate 					new_state = leaky_zalloc(
3567c478bd9Sstevel@tonic-gate 					    sizeof (*state), UM_SLEEP | UM_GC);
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 				new_state->lks_next = state;
3597c478bd9Sstevel@tonic-gate 				state = new_state;
3607c478bd9Sstevel@tonic-gate 				state_idx = 0;
3617c478bd9Sstevel@tonic-gate 			}
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 			pushes++;
3647c478bd9Sstevel@tonic-gate 			state->lks_stack[state_idx++] = mtab_ndx;
3657c478bd9Sstevel@tonic-gate 			if (++depth > deepest)
3667c478bd9Sstevel@tonic-gate 				deepest = depth;
3677c478bd9Sstevel@tonic-gate 		}
3687c478bd9Sstevel@tonic-gate 	}
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 	/*
3717c478bd9Sstevel@tonic-gate 	 * Retrieve the next mtab index, extract its info, and loop around
3727c478bd9Sstevel@tonic-gate 	 * to process it.
3737c478bd9Sstevel@tonic-gate 	 */
3747c478bd9Sstevel@tonic-gate 	if (state_idx == 0 && state != NULL) {
3757c478bd9Sstevel@tonic-gate 		new_state = state->lks_next;
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 		state->lks_next = lk_free_state;
3787c478bd9Sstevel@tonic-gate 		lk_free_state = state;
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 		state = new_state;
3817c478bd9Sstevel@tonic-gate 		state_idx = LK_STATE_SIZE;
3827c478bd9Sstevel@tonic-gate 	}
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	if (depth > 0) {
3857c478bd9Sstevel@tonic-gate 		mtab_ndx = state->lks_stack[--state_idx];
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 		addr = LK_ADDR(lk_mtab[mtab_ndx].lkm_base);
3887c478bd9Sstevel@tonic-gate 		size = lk_mtab[mtab_ndx].lkm_limit - addr;
3897c478bd9Sstevel@tonic-gate 		depth--;
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 		goto pop;
3927c478bd9Sstevel@tonic-gate 	}
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 	/*
3957c478bd9Sstevel@tonic-gate 	 * update the beans
3967c478bd9Sstevel@tonic-gate 	 */
3977c478bd9Sstevel@tonic-gate 	lk_beans.lkb_dups += dups;
3987c478bd9Sstevel@tonic-gate 	lk_beans.lkb_dismissals += dismissals;
3997c478bd9Sstevel@tonic-gate 	lk_beans.lkb_misses += misses;
4007c478bd9Sstevel@tonic-gate 	lk_beans.lkb_follows += follows;
4017c478bd9Sstevel@tonic-gate 	lk_beans.lkb_pushes += pushes;
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	if (deepest > lk_beans.lkb_deepest)
4047c478bd9Sstevel@tonic-gate 		lk_beans.lkb_deepest = deepest;
4057c478bd9Sstevel@tonic-gate }
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate static void
leaky_do_grep_ptr(uintptr_t loc,int process)4087c478bd9Sstevel@tonic-gate leaky_do_grep_ptr(uintptr_t loc, int process)
4097c478bd9Sstevel@tonic-gate {
4107c478bd9Sstevel@tonic-gate 	leak_ndx_t ndx;
4117c478bd9Sstevel@tonic-gate 	leak_mtab_t *lkmp;
4127c478bd9Sstevel@tonic-gate 	size_t sz;
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	if (loc < LK_ADDR(lk_mtab[0].lkm_base) ||
4157c478bd9Sstevel@tonic-gate 	    loc > lk_mtab[lk_nbuffers - 1].lkm_limit) {
4167c478bd9Sstevel@tonic-gate 		lk_beans.lkb_dismissals++;
4177c478bd9Sstevel@tonic-gate 		return;
4187c478bd9Sstevel@tonic-gate 	}
4197c478bd9Sstevel@tonic-gate 	if ((ndx = leaky_search(loc)) == -1) {
4207c478bd9Sstevel@tonic-gate 		lk_beans.lkb_misses++;
4217c478bd9Sstevel@tonic-gate 		return;
4227c478bd9Sstevel@tonic-gate 	}
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	lkmp = &lk_mtab[ndx];
4257c478bd9Sstevel@tonic-gate 	sz = lkmp->lkm_limit - lkmp->lkm_base;
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 	if (LK_MARKED(lkmp->lkm_base)) {
4287c478bd9Sstevel@tonic-gate 		lk_beans.lkb_dups++;
4297c478bd9Sstevel@tonic-gate 	} else {
4307c478bd9Sstevel@tonic-gate 		LK_MARK(lkmp->lkm_base);
4317c478bd9Sstevel@tonic-gate 		lk_beans.lkb_follows++;
4327c478bd9Sstevel@tonic-gate 		if (process)
4337c478bd9Sstevel@tonic-gate 			leaky_grep(lkmp->lkm_base, sz);
4347c478bd9Sstevel@tonic-gate 	}
4357c478bd9Sstevel@tonic-gate }
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate void
leaky_grep_ptr(uintptr_t loc)4387c478bd9Sstevel@tonic-gate leaky_grep_ptr(uintptr_t loc)
4397c478bd9Sstevel@tonic-gate {
4407c478bd9Sstevel@tonic-gate 	leaky_do_grep_ptr(loc, 1);
4417c478bd9Sstevel@tonic-gate }
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate void
leaky_mark_ptr(uintptr_t loc)4447c478bd9Sstevel@tonic-gate leaky_mark_ptr(uintptr_t loc)
4457c478bd9Sstevel@tonic-gate {
4467c478bd9Sstevel@tonic-gate 	leaky_do_grep_ptr(loc, 0);
4477c478bd9Sstevel@tonic-gate }
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate /*
4507c478bd9Sstevel@tonic-gate  * This may be used to manually process a marked buffer.
4517c478bd9Sstevel@tonic-gate  */
4527c478bd9Sstevel@tonic-gate int
leaky_lookup_marked(uintptr_t loc,uintptr_t * addr_out,size_t * size_out)4537c478bd9Sstevel@tonic-gate leaky_lookup_marked(uintptr_t loc, uintptr_t *addr_out, size_t *size_out)
4547c478bd9Sstevel@tonic-gate {
4557c478bd9Sstevel@tonic-gate 	leak_ndx_t ndx;
4567c478bd9Sstevel@tonic-gate 	leak_mtab_t *lkmp;
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	if ((ndx = leaky_search(loc)) == -1)
4597c478bd9Sstevel@tonic-gate 		return (0);
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 	lkmp = &lk_mtab[ndx];
4627c478bd9Sstevel@tonic-gate 	*addr_out = LK_ADDR(lkmp->lkm_base);
4637c478bd9Sstevel@tonic-gate 	*size_out = lkmp->lkm_limit - LK_ADDR(lkmp->lkm_base);
4647c478bd9Sstevel@tonic-gate 	return (1);
4657c478bd9Sstevel@tonic-gate }
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate void
leaky_add_leak(int type,uintptr_t addr,uintptr_t bufaddr,hrtime_t timestamp,leak_pc_t * stack,uint_t depth,uintptr_t cid,uintptr_t data)4687c478bd9Sstevel@tonic-gate leaky_add_leak(int type, uintptr_t addr, uintptr_t bufaddr, hrtime_t timestamp,
4697c478bd9Sstevel@tonic-gate     leak_pc_t *stack, uint_t depth, uintptr_t cid, uintptr_t data)
4707c478bd9Sstevel@tonic-gate {
4717c478bd9Sstevel@tonic-gate 	leak_bufctl_t *nlkb, *lkb;
4727c478bd9Sstevel@tonic-gate 	uintptr_t total = 0;
4737c478bd9Sstevel@tonic-gate 	size_t ndx;
4747c478bd9Sstevel@tonic-gate 	int i;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	if (type < 0 || type >= LK_NUM_TYPES || depth != (uint8_t)depth) {
4777c478bd9Sstevel@tonic-gate 		mdb_warn("invalid arguments to leaky_add_leak()\n");
4787c478bd9Sstevel@tonic-gate 		return;
4797c478bd9Sstevel@tonic-gate 	}
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 	nlkb = leaky_zalloc(LEAK_BUFCTL_SIZE(depth), UM_SLEEP);
4827c478bd9Sstevel@tonic-gate 	nlkb->lkb_type = type;
4837c478bd9Sstevel@tonic-gate 	nlkb->lkb_addr = addr;
4847c478bd9Sstevel@tonic-gate 	nlkb->lkb_bufaddr = bufaddr;
4857c478bd9Sstevel@tonic-gate 	nlkb->lkb_cid = cid;
4867c478bd9Sstevel@tonic-gate 	nlkb->lkb_data = data;
4877c478bd9Sstevel@tonic-gate 	nlkb->lkb_depth = depth;
4887c478bd9Sstevel@tonic-gate 	nlkb->lkb_timestamp = timestamp;
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 	total = type;
4917c478bd9Sstevel@tonic-gate 	for (i = 0; i < depth; i++) {
4927c478bd9Sstevel@tonic-gate 		total += stack[i];
4937c478bd9Sstevel@tonic-gate 		nlkb->lkb_stack[i] = stack[i];
4947c478bd9Sstevel@tonic-gate 	}
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	ndx = total % LK_BUFCTLHSIZE;
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	if ((lkb = lk_bufctl[ndx]) == NULL) {
4997c478bd9Sstevel@tonic-gate 		lk_types[type].lt_leaks++;
5007c478bd9Sstevel@tonic-gate 		lk_bufctl[ndx] = nlkb;
5017c478bd9Sstevel@tonic-gate 		return;
5027c478bd9Sstevel@tonic-gate 	}
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	for (;;) {
5057c478bd9Sstevel@tonic-gate 		if (lkb->lkb_type != type || lkb->lkb_depth != depth ||
5067c478bd9Sstevel@tonic-gate 		    lkb->lkb_cid != cid)
5077c478bd9Sstevel@tonic-gate 			goto no_match;
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 		for (i = 0; i < depth; i++)
5107c478bd9Sstevel@tonic-gate 			if (lkb->lkb_stack[i] != stack[i])
5117c478bd9Sstevel@tonic-gate 				goto no_match;
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 		/*
5147c478bd9Sstevel@tonic-gate 		 * If we're here, we've found a matching stack; link it in.
5157c478bd9Sstevel@tonic-gate 		 * Note that the volatile cast assures that these stores
5167c478bd9Sstevel@tonic-gate 		 * will occur in program order (thus assuring that we can
5177c478bd9Sstevel@tonic-gate 		 * take an interrupt and still be in a sane enough state to
5187c478bd9Sstevel@tonic-gate 		 * throw away the data structure later, in leaky_cleanup()).
5197c478bd9Sstevel@tonic-gate 		 */
5207c478bd9Sstevel@tonic-gate 		((volatile leak_bufctl_t *)nlkb)->lkb_next = lkb->lkb_next;
5217c478bd9Sstevel@tonic-gate 		((volatile leak_bufctl_t *)lkb)->lkb_next = nlkb;
5227c478bd9Sstevel@tonic-gate 		lkb->lkb_dups++;
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 		/*
5257c478bd9Sstevel@tonic-gate 		 * If we're older, swap places so that we are the
5267c478bd9Sstevel@tonic-gate 		 * representative leak.
5277c478bd9Sstevel@tonic-gate 		 */
5287c478bd9Sstevel@tonic-gate 		if (timestamp < lkb->lkb_timestamp) {
5297c478bd9Sstevel@tonic-gate 			nlkb->lkb_addr = lkb->lkb_addr;
5307c478bd9Sstevel@tonic-gate 			nlkb->lkb_bufaddr = lkb->lkb_bufaddr;
5317c478bd9Sstevel@tonic-gate 			nlkb->lkb_data = lkb->lkb_data;
5327c478bd9Sstevel@tonic-gate 			nlkb->lkb_timestamp = lkb->lkb_timestamp;
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 			lkb->lkb_addr = addr;
5357c478bd9Sstevel@tonic-gate 			lkb->lkb_bufaddr = bufaddr;
5367c478bd9Sstevel@tonic-gate 			lkb->lkb_data = data;
5377c478bd9Sstevel@tonic-gate 			lkb->lkb_timestamp = timestamp;
5387c478bd9Sstevel@tonic-gate 		}
5397c478bd9Sstevel@tonic-gate 		break;
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate no_match:
5427c478bd9Sstevel@tonic-gate 		if (lkb->lkb_hash_next == NULL) {
5437c478bd9Sstevel@tonic-gate 			lkb->lkb_hash_next = nlkb;
5447c478bd9Sstevel@tonic-gate 			lk_types[type].lt_leaks++;
5457c478bd9Sstevel@tonic-gate 			break;
5467c478bd9Sstevel@tonic-gate 		}
5477c478bd9Sstevel@tonic-gate 		lkb = lkb->lkb_hash_next;
5487c478bd9Sstevel@tonic-gate 	}
5497c478bd9Sstevel@tonic-gate }
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate int
leaky_ctlcmp(const void * l,const void * r)5527c478bd9Sstevel@tonic-gate leaky_ctlcmp(const void *l, const void *r)
5537c478bd9Sstevel@tonic-gate {
5547c478bd9Sstevel@tonic-gate 	const leak_bufctl_t *lhs = *((const leak_bufctl_t **)l);
5557c478bd9Sstevel@tonic-gate 	const leak_bufctl_t *rhs = *((const leak_bufctl_t **)r);
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	return (leaky_subr_bufctl_cmp(lhs, rhs));
5587c478bd9Sstevel@tonic-gate }
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate void
leaky_sort(void)5617c478bd9Sstevel@tonic-gate leaky_sort(void)
5627c478bd9Sstevel@tonic-gate {
5637c478bd9Sstevel@tonic-gate 	int type, i, j;
5647c478bd9Sstevel@tonic-gate 	leak_bufctl_t *lkb;
5657c478bd9Sstevel@tonic-gate 	leak_type_t *ltp;
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 	for (type = 0; type < LK_NUM_TYPES; type++) {
5687c478bd9Sstevel@tonic-gate 		ltp = &lk_types[type];
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 		if (ltp->lt_leaks == 0)
5717c478bd9Sstevel@tonic-gate 			continue;
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 		ltp->lt_sorted = leaky_alloc(ltp->lt_leaks *
5747c478bd9Sstevel@tonic-gate 		    sizeof (leak_bufctl_t *), UM_SLEEP);
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 		j = 0;
5777c478bd9Sstevel@tonic-gate 		for (i = 0; i < LK_BUFCTLHSIZE; i++) {
5787c478bd9Sstevel@tonic-gate 			for (lkb = lk_bufctl[i]; lkb != NULL;
5797c478bd9Sstevel@tonic-gate 			    lkb = lkb->lkb_hash_next) {
5807c478bd9Sstevel@tonic-gate 				if (lkb->lkb_type == type)
5817c478bd9Sstevel@tonic-gate 					ltp->lt_sorted[j++] = lkb;
5827c478bd9Sstevel@tonic-gate 			}
5837c478bd9Sstevel@tonic-gate 		}
5847c478bd9Sstevel@tonic-gate 		if (j != ltp->lt_leaks)
5857c478bd9Sstevel@tonic-gate 			mdb_warn("expected %d leaks, got %d\n", ltp->lt_leaks,
5867c478bd9Sstevel@tonic-gate 			    j);
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 		qsort(ltp->lt_sorted, ltp->lt_leaks, sizeof (leak_bufctl_t *),
5897c478bd9Sstevel@tonic-gate 		    leaky_ctlcmp);
5907c478bd9Sstevel@tonic-gate 	}
5917c478bd9Sstevel@tonic-gate }
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate void
leaky_cleanup(int force)5947c478bd9Sstevel@tonic-gate leaky_cleanup(int force)
5957c478bd9Sstevel@tonic-gate {
5967c478bd9Sstevel@tonic-gate 	int i;
5977c478bd9Sstevel@tonic-gate 	leak_bufctl_t *lkb, *l, *next;
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 	/*
6007c478bd9Sstevel@tonic-gate 	 * State structures are allocated UM_GC, so we just need to nuke
6017c478bd9Sstevel@tonic-gate 	 * the freelist pointer.
6027c478bd9Sstevel@tonic-gate 	 */
6037c478bd9Sstevel@tonic-gate 	lk_free_state = NULL;
6047c478bd9Sstevel@tonic-gate 
605*789d94c2Sjwadams 	switch (lk_state) {
606*789d94c2Sjwadams 	case LK_CLEAN:
607*789d94c2Sjwadams 		return;		/* nothing to do */
608*789d94c2Sjwadams 
609*789d94c2Sjwadams 	case LK_CLEANING:
6107c478bd9Sstevel@tonic-gate 		mdb_warn("interrupted during ::findleaks cleanup; some mdb "
6117c478bd9Sstevel@tonic-gate 		    "memory will be leaked\n");
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 		for (i = 0; i < LK_BUFCTLHSIZE; i++)
6147c478bd9Sstevel@tonic-gate 			lk_bufctl[i] = NULL;
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 		for (i = 0; i < LK_NUM_TYPES; i++) {
6177c478bd9Sstevel@tonic-gate 			lk_types[i].lt_leaks = 0;
6187c478bd9Sstevel@tonic-gate 			lk_types[i].lt_sorted = NULL;
6197c478bd9Sstevel@tonic-gate 		}
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 		bzero(&lk_beans, sizeof (lk_beans));
6227c478bd9Sstevel@tonic-gate 		lk_state = LK_CLEAN;
6237c478bd9Sstevel@tonic-gate 		return;
6247c478bd9Sstevel@tonic-gate 
625*789d94c2Sjwadams 	case LK_SWEEPING:
626*789d94c2Sjwadams 		break;		/* must clean up */
627*789d94c2Sjwadams 
628*789d94c2Sjwadams 	case LK_DONE:
629*789d94c2Sjwadams 	default:
630*789d94c2Sjwadams 		if (!force)
6317c478bd9Sstevel@tonic-gate 			return;
632*789d94c2Sjwadams 		break;		/* only clean up if forced */
633*789d94c2Sjwadams 	}
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate 	lk_state = LK_CLEANING;
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	for (i = 0; i < LK_NUM_TYPES; i++) {
6387c478bd9Sstevel@tonic-gate 		if (lk_types[i].lt_sorted != NULL) {
6397c478bd9Sstevel@tonic-gate 			mdb_free(lk_types[i].lt_sorted,
6407c478bd9Sstevel@tonic-gate 			    lk_types[i].lt_leaks * sizeof (leak_bufctl_t *));
6417c478bd9Sstevel@tonic-gate 			lk_types[i].lt_sorted = NULL;
6427c478bd9Sstevel@tonic-gate 		}
6437c478bd9Sstevel@tonic-gate 		lk_types[i].lt_leaks = 0;
6447c478bd9Sstevel@tonic-gate 	}
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	for (i = 0; i < LK_BUFCTLHSIZE; i++) {
6477c478bd9Sstevel@tonic-gate 		for (lkb = lk_bufctl[i]; lkb != NULL; lkb = next) {
6487c478bd9Sstevel@tonic-gate 			for (l = lkb->lkb_next; l != NULL; l = next) {
6497c478bd9Sstevel@tonic-gate 				next = l->lkb_next;
6507c478bd9Sstevel@tonic-gate 				mdb_free(l, LEAK_BUFCTL_SIZE(l->lkb_depth));
6517c478bd9Sstevel@tonic-gate 			}
6527c478bd9Sstevel@tonic-gate 			next = lkb->lkb_hash_next;
6537c478bd9Sstevel@tonic-gate 			mdb_free(lkb, LEAK_BUFCTL_SIZE(lkb->lkb_depth));
6547c478bd9Sstevel@tonic-gate 		}
6557c478bd9Sstevel@tonic-gate 		lk_bufctl[i] = NULL;
6567c478bd9Sstevel@tonic-gate 	}
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate 	bzero(&lk_beans, sizeof (lk_beans));
6597c478bd9Sstevel@tonic-gate 	lk_state = LK_CLEAN;
6607c478bd9Sstevel@tonic-gate }
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate int
leaky_filter(const leak_pc_t * stack,int depth,uintptr_t filter)6637c478bd9Sstevel@tonic-gate leaky_filter(const leak_pc_t *stack, int depth, uintptr_t filter)
6647c478bd9Sstevel@tonic-gate {
6657c478bd9Sstevel@tonic-gate 	int i;
6667c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
6677c478bd9Sstevel@tonic-gate 	char c;
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	if (filter == NULL)
6707c478bd9Sstevel@tonic-gate 		return (1);
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 	for (i = 0; i < depth; i++) {
6737c478bd9Sstevel@tonic-gate 		if (stack[i] == filter)
6747c478bd9Sstevel@tonic-gate 			return (1);
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 		if (mdb_lookup_by_addr(stack[i], MDB_SYM_FUZZY,
6777c478bd9Sstevel@tonic-gate 		    &c, sizeof (c), &sym) == -1)
6787c478bd9Sstevel@tonic-gate 			continue;
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 		if ((uintptr_t)sym.st_value == filter)
6817c478bd9Sstevel@tonic-gate 			return (1);
6827c478bd9Sstevel@tonic-gate 	}
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	return (0);
6857c478bd9Sstevel@tonic-gate }
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate void
leaky_dump(uintptr_t filter,uint_t dump_verbose)6887c478bd9Sstevel@tonic-gate leaky_dump(uintptr_t filter, uint_t dump_verbose)
6897c478bd9Sstevel@tonic-gate {
6907c478bd9Sstevel@tonic-gate 	int i;
6917c478bd9Sstevel@tonic-gate 	size_t leaks;
6927c478bd9Sstevel@tonic-gate 	leak_bufctl_t **sorted;
6937c478bd9Sstevel@tonic-gate 	leak_bufctl_t *lkb;
6947c478bd9Sstevel@tonic-gate 	int seen = 0;
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	for (i = 0; i < LK_NUM_TYPES; i++) {
6977c478bd9Sstevel@tonic-gate 		leaks = lk_types[i].lt_leaks;
6987c478bd9Sstevel@tonic-gate 		sorted = lk_types[i].lt_sorted;
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 		leaky_subr_dump_start(i);
7017c478bd9Sstevel@tonic-gate 		while (leaks-- > 0) {
7027c478bd9Sstevel@tonic-gate 			lkb = *sorted++;
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 			if (!leaky_filter(lkb->lkb_stack, lkb->lkb_depth,
7057c478bd9Sstevel@tonic-gate 			    filter))
7067c478bd9Sstevel@tonic-gate 				continue;
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 			seen = 1;
7097c478bd9Sstevel@tonic-gate 			leaky_subr_dump(lkb, 0);
7107c478bd9Sstevel@tonic-gate 		}
7117c478bd9Sstevel@tonic-gate 		leaky_subr_dump_end(i);
7127c478bd9Sstevel@tonic-gate 	}
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 	if (!seen) {
7157c478bd9Sstevel@tonic-gate 		if (filter != NULL)
7167c478bd9Sstevel@tonic-gate 			mdb_printf(
7177c478bd9Sstevel@tonic-gate 			    "findleaks: no memory leaks matching %a found\n",
7187c478bd9Sstevel@tonic-gate 			    filter);
7197c478bd9Sstevel@tonic-gate 		else
7207c478bd9Sstevel@tonic-gate 			mdb_printf(
7217c478bd9Sstevel@tonic-gate 			    "findleaks: no memory leaks detected\n");
7227c478bd9Sstevel@tonic-gate 	}
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	if (!dump_verbose || !seen)
7257c478bd9Sstevel@tonic-gate 		return;
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 	mdb_printf("\n");
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 	for (i = 0; i < LK_NUM_TYPES; i++) {
7307c478bd9Sstevel@tonic-gate 		leaks = lk_types[i].lt_leaks;
7317c478bd9Sstevel@tonic-gate 		sorted = lk_types[i].lt_sorted;
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 		while (leaks-- > 0) {
7347c478bd9Sstevel@tonic-gate 			lkb = *sorted++;
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate 			if (!leaky_filter(lkb->lkb_stack, lkb->lkb_depth,
7377c478bd9Sstevel@tonic-gate 			    filter))
7387c478bd9Sstevel@tonic-gate 				continue;
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate 			leaky_subr_dump(lkb, 1);
7417c478bd9Sstevel@tonic-gate 		}
7427c478bd9Sstevel@tonic-gate 	}
7437c478bd9Sstevel@tonic-gate }
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate static const char *const findleaks_desc =
7467c478bd9Sstevel@tonic-gate 	"Does a conservative garbage collection of the heap in order to find\n"
7477c478bd9Sstevel@tonic-gate 	"potentially leaked buffers.  Similar leaks are coalesced by stack\n"
7487c478bd9Sstevel@tonic-gate 	"trace, with the oldest leak picked as representative.  The leak\n"
7497c478bd9Sstevel@tonic-gate 	"table is cached between invocations.\n"
7507c478bd9Sstevel@tonic-gate 	"\n"
7517c478bd9Sstevel@tonic-gate 	"addr, if provided, should be a function or PC location.  Reported\n"
7527c478bd9Sstevel@tonic-gate 	"leaks will then be limited to those with that function or PC in\n"
7537c478bd9Sstevel@tonic-gate 	"their stack trace.\n"
7547c478bd9Sstevel@tonic-gate 	"\n"
7557c478bd9Sstevel@tonic-gate 	"The 'leak' and 'leakbuf' walkers can be used to retrieve coalesced\n"
7567c478bd9Sstevel@tonic-gate 	"leaks.\n";
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate static const char *const findleaks_args =
7597c478bd9Sstevel@tonic-gate 	"  -d    detail each representative leak (long)\n"
7607c478bd9Sstevel@tonic-gate 	"  -f    throw away cached state, and do a full run\n"
7617c478bd9Sstevel@tonic-gate 	"  -v    report verbose information about the findleaks run\n";
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate void
findleaks_help(void)7647c478bd9Sstevel@tonic-gate findleaks_help(void)
7657c478bd9Sstevel@tonic-gate {
7667c478bd9Sstevel@tonic-gate 	mdb_printf("%s\n", findleaks_desc);
7677c478bd9Sstevel@tonic-gate 	mdb_dec_indent(2);
7687c478bd9Sstevel@tonic-gate 	mdb_printf("%<b>OPTIONS%</b>\n");
7697c478bd9Sstevel@tonic-gate 	mdb_inc_indent(2);
7707c478bd9Sstevel@tonic-gate 	mdb_printf("%s", findleaks_args);
7717c478bd9Sstevel@tonic-gate }
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate #define	LK_REPORT_BEAN(x) leaky_verbose_perc(#x, lk_beans.lkb_##x, total);
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7767c478bd9Sstevel@tonic-gate int
findleaks(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)7777c478bd9Sstevel@tonic-gate findleaks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
7787c478bd9Sstevel@tonic-gate {
7797c478bd9Sstevel@tonic-gate 	size_t est = 0;
7807c478bd9Sstevel@tonic-gate 	leak_ndx_t i;
7817c478bd9Sstevel@tonic-gate 	leak_mtab_t *lmp;
7827c478bd9Sstevel@tonic-gate 	ssize_t total;
7837c478bd9Sstevel@tonic-gate 	uintptr_t filter = NULL;
7847c478bd9Sstevel@tonic-gate 	uint_t dump = 0;
7857c478bd9Sstevel@tonic-gate 	uint_t force = 0;
7867c478bd9Sstevel@tonic-gate 	uint_t verbose = 0;
7877c478bd9Sstevel@tonic-gate 	int ret;
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
7907c478bd9Sstevel@tonic-gate 		filter = addr;
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 	if (mdb_getopts(argc, argv,
7937c478bd9Sstevel@tonic-gate 	    'd', MDB_OPT_SETBITS, TRUE, &dump,
7947c478bd9Sstevel@tonic-gate 	    'f', MDB_OPT_SETBITS, TRUE, &force,
7957c478bd9Sstevel@tonic-gate 	    'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL) != argc)
7967c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 	if (verbose || force)
7997c478bd9Sstevel@tonic-gate 		lk_verbose = verbose;
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	/*
8027c478bd9Sstevel@tonic-gate 	 * Clean any previous ::findleaks.
8037c478bd9Sstevel@tonic-gate 	 */
8047c478bd9Sstevel@tonic-gate 	leaky_cleanup(force);
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 	if (lk_state == LK_DONE) {
8077c478bd9Sstevel@tonic-gate 		if (lk_verbose)
8087c478bd9Sstevel@tonic-gate 			mdb_printf("findleaks: using cached results "
809*789d94c2Sjwadams 			    "(use '-f' to force a full run)\n");
8107c478bd9Sstevel@tonic-gate 		goto dump;
8117c478bd9Sstevel@tonic-gate 	}
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate 	leaky_verbose_begin();
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 	if ((ret = leaky_subr_estimate(&est)) != DCMD_OK)
8167c478bd9Sstevel@tonic-gate 		return (ret);
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 	leaky_verbose("maximum buffers", est);
8197c478bd9Sstevel@tonic-gate 
8207c478bd9Sstevel@tonic-gate 	/*
8217c478bd9Sstevel@tonic-gate 	 * Now we have an upper bound on the number of buffers.  Allocate
8227c478bd9Sstevel@tonic-gate 	 * our mtab array.
8237c478bd9Sstevel@tonic-gate 	 */
8247c478bd9Sstevel@tonic-gate 	lk_mtab = leaky_zalloc(est * sizeof (leak_mtab_t), UM_SLEEP | UM_GC);
8257c478bd9Sstevel@tonic-gate 	lmp = lk_mtab;
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 	if ((ret = leaky_subr_fill(&lmp)) != DCMD_OK)
8287c478bd9Sstevel@tonic-gate 		return (ret);
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 	lk_nbuffers = lmp - lk_mtab;
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	qsort(lk_mtab, lk_nbuffers, sizeof (leak_mtab_t), leaky_mtabcmp);
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 	/*
8357c478bd9Sstevel@tonic-gate 	 * validate the mtab table now that it is sorted
8367c478bd9Sstevel@tonic-gate 	 */
8377c478bd9Sstevel@tonic-gate 	for (i = 0; i < lk_nbuffers; i++) {
8387c478bd9Sstevel@tonic-gate 		if (lk_mtab[i].lkm_base >= lk_mtab[i].lkm_limit) {
8397c478bd9Sstevel@tonic-gate 			mdb_warn("[%p, %p): invalid mtab\n",
8407c478bd9Sstevel@tonic-gate 			    lk_mtab[i].lkm_base, lk_mtab[i].lkm_limit);
8417c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
8427c478bd9Sstevel@tonic-gate 		}
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 		if (i < lk_nbuffers - 1 &&
8457c478bd9Sstevel@tonic-gate 		    lk_mtab[i].lkm_limit > lk_mtab[i + 1].lkm_base) {
8467c478bd9Sstevel@tonic-gate 			mdb_warn("[%p, %p) and [%p, %p): overlapping mtabs\n",
8477c478bd9Sstevel@tonic-gate 			    lk_mtab[i].lkm_base, lk_mtab[i].lkm_limit,
8487c478bd9Sstevel@tonic-gate 			    lk_mtab[i + 1].lkm_base, lk_mtab[i + 1].lkm_limit);
8497c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
8507c478bd9Sstevel@tonic-gate 		}
8517c478bd9Sstevel@tonic-gate 	}
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	leaky_verbose("actual buffers", lk_nbuffers);
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	lk_scan_buffer = leaky_zalloc(LK_SCAN_BUFFER_SIZE, UM_SLEEP | UM_GC);
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate 	if ((ret = leaky_subr_run()) != DCMD_OK)
8587c478bd9Sstevel@tonic-gate 		return (ret);
8597c478bd9Sstevel@tonic-gate 
8607c478bd9Sstevel@tonic-gate 	lk_state = LK_SWEEPING;
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	for (i = 0; i < lk_nbuffers; i++) {
8637c478bd9Sstevel@tonic-gate 		if (LK_MARKED(lk_mtab[i].lkm_base))
8647c478bd9Sstevel@tonic-gate 			continue;
8657c478bd9Sstevel@tonic-gate 		leaky_subr_add_leak(&lk_mtab[i]);
8667c478bd9Sstevel@tonic-gate 	}
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	total = lk_beans.lkb_dismissals + lk_beans.lkb_misses +
8697c478bd9Sstevel@tonic-gate 	    lk_beans.lkb_dups + lk_beans.lkb_follows;
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	leaky_verbose(NULL, 0);
8727c478bd9Sstevel@tonic-gate 	leaky_verbose("potential pointers", total);
8737c478bd9Sstevel@tonic-gate 	LK_REPORT_BEAN(dismissals);
8747c478bd9Sstevel@tonic-gate 	LK_REPORT_BEAN(misses);
8757c478bd9Sstevel@tonic-gate 	LK_REPORT_BEAN(dups);
8767c478bd9Sstevel@tonic-gate 	LK_REPORT_BEAN(follows);
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 	leaky_verbose(NULL, 0);
8797c478bd9Sstevel@tonic-gate 	leaky_verbose_end();
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate 	leaky_sort();
8827c478bd9Sstevel@tonic-gate 	lk_state = LK_DONE;
8837c478bd9Sstevel@tonic-gate dump:
8847c478bd9Sstevel@tonic-gate 	leaky_dump(filter, dump);
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
8877c478bd9Sstevel@tonic-gate }
8887c478bd9Sstevel@tonic-gate 
8897c478bd9Sstevel@tonic-gate int
leaky_walk_init(mdb_walk_state_t * wsp)8907c478bd9Sstevel@tonic-gate leaky_walk_init(mdb_walk_state_t *wsp)
8917c478bd9Sstevel@tonic-gate {
8927c478bd9Sstevel@tonic-gate 	leak_walk_t *lw;
8937c478bd9Sstevel@tonic-gate 	leak_bufctl_t *lkb, *cur;
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 	uintptr_t addr;
8967c478bd9Sstevel@tonic-gate 	int i;
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 	if (lk_state != LK_DONE) {
8997c478bd9Sstevel@tonic-gate 		mdb_warn("::findleaks must be run %sbefore leaks can be"
9007c478bd9Sstevel@tonic-gate 		    " walked\n", lk_state != LK_CLEAN ? "to completion " : "");
9017c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
9027c478bd9Sstevel@tonic-gate 	}
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 	if (wsp->walk_addr == NULL) {
9057c478bd9Sstevel@tonic-gate 		lkb = NULL;
9067c478bd9Sstevel@tonic-gate 		goto found;
9077c478bd9Sstevel@tonic-gate 	}
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	addr = wsp->walk_addr;
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 	/*
9127c478bd9Sstevel@tonic-gate 	 * Search the representative leaks first, since that's what we
9137c478bd9Sstevel@tonic-gate 	 * report in the table.  If that fails, search everything.
9147c478bd9Sstevel@tonic-gate 	 *
9157c478bd9Sstevel@tonic-gate 	 * Note that we goto found with lkb as the head of desired dup list.
9167c478bd9Sstevel@tonic-gate 	 */
9177c478bd9Sstevel@tonic-gate 	for (i = 0; i < LK_BUFCTLHSIZE; i++) {
9187c478bd9Sstevel@tonic-gate 		for (lkb = lk_bufctl[i]; lkb != NULL; lkb = lkb->lkb_hash_next)
9197c478bd9Sstevel@tonic-gate 			if (lkb->lkb_addr == addr)
9207c478bd9Sstevel@tonic-gate 				goto found;
9217c478bd9Sstevel@tonic-gate 	}
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 	for (i = 0; i < LK_BUFCTLHSIZE; i++) {
9247c478bd9Sstevel@tonic-gate 		for (lkb = lk_bufctl[i]; lkb != NULL; lkb = lkb->lkb_hash_next)
9257c478bd9Sstevel@tonic-gate 			for (cur = lkb; cur != NULL; cur = cur->lkb_next)
9267c478bd9Sstevel@tonic-gate 				if (cur->lkb_addr == addr)
9277c478bd9Sstevel@tonic-gate 					goto found;
9287c478bd9Sstevel@tonic-gate 	}
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 	mdb_warn("%p is not a leaked ctl address\n", addr);
9317c478bd9Sstevel@tonic-gate 	return (WALK_ERR);
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate found:
9347c478bd9Sstevel@tonic-gate 	wsp->walk_data = lw = mdb_zalloc(sizeof (*lw), UM_SLEEP);
9357c478bd9Sstevel@tonic-gate 	lw->lkw_ndx = 0;
9367c478bd9Sstevel@tonic-gate 	lw->lkw_current = lkb;
9377c478bd9Sstevel@tonic-gate 	lw->lkw_hash_next = NULL;
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
9407c478bd9Sstevel@tonic-gate }
9417c478bd9Sstevel@tonic-gate 
9427c478bd9Sstevel@tonic-gate leak_bufctl_t *
leaky_walk_step_common(mdb_walk_state_t * wsp)9437c478bd9Sstevel@tonic-gate leaky_walk_step_common(mdb_walk_state_t *wsp)
9447c478bd9Sstevel@tonic-gate {
9457c478bd9Sstevel@tonic-gate 	leak_walk_t *lw = wsp->walk_data;
9467c478bd9Sstevel@tonic-gate 	leak_bufctl_t *lk;
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	if ((lk = lw->lkw_current) == NULL) {
9497c478bd9Sstevel@tonic-gate 		if ((lk = lw->lkw_hash_next) == NULL) {
9507c478bd9Sstevel@tonic-gate 			if (wsp->walk_addr)
9517c478bd9Sstevel@tonic-gate 				return (NULL);
9527c478bd9Sstevel@tonic-gate 
9537c478bd9Sstevel@tonic-gate 			while (lk == NULL && lw->lkw_ndx < LK_BUFCTLHSIZE)
9547c478bd9Sstevel@tonic-gate 				lk = lk_bufctl[lw->lkw_ndx++];
9557c478bd9Sstevel@tonic-gate 
9567c478bd9Sstevel@tonic-gate 			if (lw->lkw_ndx == LK_BUFCTLHSIZE)
9577c478bd9Sstevel@tonic-gate 				return (NULL);
9587c478bd9Sstevel@tonic-gate 		}
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 		lw->lkw_hash_next = lk->lkb_hash_next;
9617c478bd9Sstevel@tonic-gate 	}
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate 	lw->lkw_current = lk->lkb_next;
9647c478bd9Sstevel@tonic-gate 	return (lk);
9657c478bd9Sstevel@tonic-gate }
9667c478bd9Sstevel@tonic-gate 
9677c478bd9Sstevel@tonic-gate int
leaky_walk_step(mdb_walk_state_t * wsp)9687c478bd9Sstevel@tonic-gate leaky_walk_step(mdb_walk_state_t *wsp)
9697c478bd9Sstevel@tonic-gate {
9707c478bd9Sstevel@tonic-gate 	leak_bufctl_t *lk;
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 	if ((lk = leaky_walk_step_common(wsp)) == NULL)
9737c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
9747c478bd9Sstevel@tonic-gate 
9757c478bd9Sstevel@tonic-gate 	return (leaky_subr_invoke_callback(lk, wsp->walk_callback,
9767c478bd9Sstevel@tonic-gate 	    wsp->walk_cbdata));
9777c478bd9Sstevel@tonic-gate }
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate void
leaky_walk_fini(mdb_walk_state_t * wsp)9807c478bd9Sstevel@tonic-gate leaky_walk_fini(mdb_walk_state_t *wsp)
9817c478bd9Sstevel@tonic-gate {
9827c478bd9Sstevel@tonic-gate 	leak_walk_t *lw = wsp->walk_data;
9837c478bd9Sstevel@tonic-gate 
9847c478bd9Sstevel@tonic-gate 	mdb_free(lw, sizeof (leak_walk_t));
9857c478bd9Sstevel@tonic-gate }
9867c478bd9Sstevel@tonic-gate 
9877c478bd9Sstevel@tonic-gate int
leaky_buf_walk_step(mdb_walk_state_t * wsp)9887c478bd9Sstevel@tonic-gate leaky_buf_walk_step(mdb_walk_state_t *wsp)
9897c478bd9Sstevel@tonic-gate {
9907c478bd9Sstevel@tonic-gate 	leak_bufctl_t *lk;
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 	if ((lk = leaky_walk_step_common(wsp)) == NULL)
9937c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	return (wsp->walk_callback(lk->lkb_bufaddr, NULL, wsp->walk_cbdata));
9967c478bd9Sstevel@tonic-gate }
997