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