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 #include "umem.h"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <sys/vmem_impl_user.h>
297c478bd9Sstevel@tonic-gate #include <umem_impl.h>
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <alloca.h>
327c478bd9Sstevel@tonic-gate #include <libproc.h>
337c478bd9Sstevel@tonic-gate #include <stdio.h>
347c478bd9Sstevel@tonic-gate #include <string.h>
357c478bd9Sstevel@tonic-gate #include <sys/stack.h>
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include "leaky_impl.h"
387c478bd9Sstevel@tonic-gate #include "misc.h"
397c478bd9Sstevel@tonic-gate #include "proc_kludges.h"
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate #include "umem_pagesize.h"
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate /*
447c478bd9Sstevel@tonic-gate  * This file defines the libumem target for ../genunix/leaky.c.
457c478bd9Sstevel@tonic-gate  *
467c478bd9Sstevel@tonic-gate  * See ../genunix/leaky_impl.h for the target interface definition.
477c478bd9Sstevel@tonic-gate  */
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate /*
507c478bd9Sstevel@tonic-gate  * leaky_subr_dump_start()/_end() depend on the ordering of TYPE_VMEM,
517c478bd9Sstevel@tonic-gate  * TYPE_MMAP and TYPE_SBRK.
527c478bd9Sstevel@tonic-gate  */
537c478bd9Sstevel@tonic-gate #define	TYPE_MMAP	0		/* lkb_data is the size */
547c478bd9Sstevel@tonic-gate #define	TYPE_SBRK	1		/* lkb_data is the size */
557c478bd9Sstevel@tonic-gate #define	TYPE_VMEM	2		/* lkb_data is the vmem_seg's size */
567c478bd9Sstevel@tonic-gate #define	TYPE_CACHE	3		/* lkb_cid is the bufctl's cache */
577c478bd9Sstevel@tonic-gate #define	TYPE_UMEM	4		/* lkb_cid is the bufctl's cache */
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate #define	LKM_CTL_BUFCTL	0	/* normal allocation, PTR is bufctl */
607c478bd9Sstevel@tonic-gate #define	LKM_CTL_VMSEG	1	/* oversize allocation, PTR is vmem_seg_t */
617c478bd9Sstevel@tonic-gate #define	LKM_CTL_MEMORY	2	/* non-umem mmap or brk, PTR is region start */
627c478bd9Sstevel@tonic-gate #define	LKM_CTL_CACHE	3	/* normal alloc, non-debug, PTR is cache */
637c478bd9Sstevel@tonic-gate #define	LKM_CTL_MASK	3L
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate /*
667c478bd9Sstevel@tonic-gate  * create a lkm_bufctl from a pointer and a type
677c478bd9Sstevel@tonic-gate  */
687c478bd9Sstevel@tonic-gate #define	LKM_CTL(ptr, type)	(LKM_CTLPTR(ptr) | (type))
697c478bd9Sstevel@tonic-gate #define	LKM_CTLPTR(ctl)		((uintptr_t)(ctl) & ~(LKM_CTL_MASK))
707c478bd9Sstevel@tonic-gate #define	LKM_CTLTYPE(ctl)	((uintptr_t)(ctl) &  (LKM_CTL_MASK))
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate static uintptr_t leak_brkbase;
737c478bd9Sstevel@tonic-gate static uintptr_t leak_brksize;
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate #define	LEAKY_INBRK(ptr) \
767c478bd9Sstevel@tonic-gate 	(((uintptr_t)(ptr) - leak_brkbase) < leak_brksize)
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate typedef struct leaky_seg_info {
797c478bd9Sstevel@tonic-gate 	uintptr_t ls_start;
807c478bd9Sstevel@tonic-gate 	uintptr_t ls_end;
817c478bd9Sstevel@tonic-gate } leaky_seg_info_t;
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate typedef struct leaky_maps {
847c478bd9Sstevel@tonic-gate 	leaky_seg_info_t	*lm_segs;
857c478bd9Sstevel@tonic-gate 	uintptr_t		lm_seg_count;
867c478bd9Sstevel@tonic-gate 	uintptr_t		lm_seg_max;
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate 	pstatus_t		*lm_pstatus;
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	leak_mtab_t		**lm_lmp;
917c478bd9Sstevel@tonic-gate } leaky_maps_t;
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*ARGSUSED*/
947c478bd9Sstevel@tonic-gate static int
leaky_mtab(uintptr_t addr,const umem_bufctl_audit_t * bcp,leak_mtab_t ** lmp)957c478bd9Sstevel@tonic-gate leaky_mtab(uintptr_t addr, const umem_bufctl_audit_t *bcp, leak_mtab_t **lmp)
967c478bd9Sstevel@tonic-gate {
977c478bd9Sstevel@tonic-gate 	leak_mtab_t *lm = (*lmp)++;
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate 	lm->lkm_base = (uintptr_t)bcp->bc_addr;
1007c478bd9Sstevel@tonic-gate 	lm->lkm_bufctl = LKM_CTL(addr, LKM_CTL_BUFCTL);
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1037c478bd9Sstevel@tonic-gate }
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1067c478bd9Sstevel@tonic-gate static int
leaky_mtab_addr(uintptr_t addr,void * ignored,leak_mtab_t ** lmp)1077c478bd9Sstevel@tonic-gate leaky_mtab_addr(uintptr_t addr, void *ignored, leak_mtab_t **lmp)
1087c478bd9Sstevel@tonic-gate {
1097c478bd9Sstevel@tonic-gate 	leak_mtab_t *lm = (*lmp)++;
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 	lm->lkm_base = addr;
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1147c478bd9Sstevel@tonic-gate }
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate static int
leaky_seg(uintptr_t addr,const vmem_seg_t * seg,leak_mtab_t ** lmp)1177c478bd9Sstevel@tonic-gate leaky_seg(uintptr_t addr, const vmem_seg_t *seg, leak_mtab_t **lmp)
1187c478bd9Sstevel@tonic-gate {
1197c478bd9Sstevel@tonic-gate 	leak_mtab_t *lm = (*lmp)++;
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 	lm->lkm_base = seg->vs_start;
1227c478bd9Sstevel@tonic-gate 	lm->lkm_limit = seg->vs_end;
1237c478bd9Sstevel@tonic-gate 	lm->lkm_bufctl = LKM_CTL(addr, LKM_CTL_VMSEG);
1247c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1257c478bd9Sstevel@tonic-gate }
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate static int
leaky_vmem(uintptr_t addr,const vmem_t * vmem,leak_mtab_t ** lmp)1287c478bd9Sstevel@tonic-gate leaky_vmem(uintptr_t addr, const vmem_t *vmem, leak_mtab_t **lmp)
1297c478bd9Sstevel@tonic-gate {
1307c478bd9Sstevel@tonic-gate 	if (strcmp(vmem->vm_name, "umem_oversize") != 0 &&
1317c478bd9Sstevel@tonic-gate 	    strcmp(vmem->vm_name, "umem_memalign") != 0)
1327c478bd9Sstevel@tonic-gate 		return (WALK_NEXT);
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	if (mdb_pwalk("vmem_alloc", (mdb_walk_cb_t)leaky_seg, lmp, addr) == -1)
1357c478bd9Sstevel@tonic-gate 		mdb_warn("can't walk vmem_alloc for %s (%p)", vmem->vm_name,
1367c478bd9Sstevel@tonic-gate 		    addr);
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1397c478bd9Sstevel@tonic-gate }
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1427c478bd9Sstevel@tonic-gate static int
leaky_estimate_vmem(uintptr_t addr,const vmem_t * vmem,size_t * est)1437c478bd9Sstevel@tonic-gate leaky_estimate_vmem(uintptr_t addr, const vmem_t *vmem, size_t *est)
1447c478bd9Sstevel@tonic-gate {
1457c478bd9Sstevel@tonic-gate 	if (strcmp(vmem->vm_name, "umem_oversize") != 0 &&
1467c478bd9Sstevel@tonic-gate 	    strcmp(vmem->vm_name, "umem_memalign") != 0)
1477c478bd9Sstevel@tonic-gate 		return (WALK_NEXT);
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 	*est += (int)(vmem->vm_kstat.vk_alloc - vmem->vm_kstat.vk_free);
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1527c478bd9Sstevel@tonic-gate }
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate static int
leaky_seg_cmp(const void * l,const void * r)1557c478bd9Sstevel@tonic-gate leaky_seg_cmp(const void *l, const void *r)
1567c478bd9Sstevel@tonic-gate {
1577c478bd9Sstevel@tonic-gate 	const leaky_seg_info_t *lhs = (const leaky_seg_info_t *)l;
1587c478bd9Sstevel@tonic-gate 	const leaky_seg_info_t *rhs = (const leaky_seg_info_t *)r;
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate 	if (lhs->ls_start < rhs->ls_start)
1617c478bd9Sstevel@tonic-gate 		return (-1);
1627c478bd9Sstevel@tonic-gate 	if (lhs->ls_start > rhs->ls_start)
1637c478bd9Sstevel@tonic-gate 		return (1);
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	return (0);
1667c478bd9Sstevel@tonic-gate }
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate static ssize_t
leaky_seg_search(uintptr_t addr,leaky_seg_info_t * listp,unsigned count)1697c478bd9Sstevel@tonic-gate leaky_seg_search(uintptr_t addr, leaky_seg_info_t *listp, unsigned count)
1707c478bd9Sstevel@tonic-gate {
1717c478bd9Sstevel@tonic-gate 	ssize_t left = 0, right = count - 1, guess;
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	while (right >= left) {
1747c478bd9Sstevel@tonic-gate 		guess = (right + left) >> 1;
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 		if (addr < listp[guess].ls_start) {
1777c478bd9Sstevel@tonic-gate 			right = guess - 1;
1787c478bd9Sstevel@tonic-gate 			continue;
1797c478bd9Sstevel@tonic-gate 		}
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 		if (addr >= listp[guess].ls_end) {
1827c478bd9Sstevel@tonic-gate 			left = guess + 1;
1837c478bd9Sstevel@tonic-gate 			continue;
1847c478bd9Sstevel@tonic-gate 		}
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 		return (guess);
1877c478bd9Sstevel@tonic-gate 	}
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	return (-1);
1907c478bd9Sstevel@tonic-gate }
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1937c478bd9Sstevel@tonic-gate static int
leaky_count(uintptr_t addr,void * unused,size_t * total)1947c478bd9Sstevel@tonic-gate leaky_count(uintptr_t addr, void *unused, size_t *total)
1957c478bd9Sstevel@tonic-gate {
1967c478bd9Sstevel@tonic-gate 	++*total;
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1997c478bd9Sstevel@tonic-gate }
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2027c478bd9Sstevel@tonic-gate static int
leaky_read_segs(uintptr_t addr,const vmem_seg_t * seg,leaky_maps_t * lmp)2037c478bd9Sstevel@tonic-gate leaky_read_segs(uintptr_t addr, const vmem_seg_t *seg, leaky_maps_t *lmp)
2047c478bd9Sstevel@tonic-gate {
2057c478bd9Sstevel@tonic-gate 	leaky_seg_info_t *my_si = lmp->lm_segs + lmp->lm_seg_count;
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	if (seg->vs_start == seg->vs_end && seg->vs_start == 0)
2087c478bd9Sstevel@tonic-gate 		return (WALK_NEXT);
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	if (lmp->lm_seg_count++ >= lmp->lm_seg_max)
2117c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	my_si->ls_start = seg->vs_start;
2147c478bd9Sstevel@tonic-gate 	my_si->ls_end = seg->vs_end;
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
2177c478bd9Sstevel@tonic-gate }
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate /* ARGSUSED */
2207c478bd9Sstevel@tonic-gate static int
leaky_process_anon_mappings(uintptr_t ignored,const prmap_t * pmp,leaky_maps_t * lmp)2217c478bd9Sstevel@tonic-gate leaky_process_anon_mappings(uintptr_t ignored, const prmap_t *pmp,
2227c478bd9Sstevel@tonic-gate     leaky_maps_t *lmp)
2237c478bd9Sstevel@tonic-gate {
2247c478bd9Sstevel@tonic-gate 	uintptr_t start = pmp->pr_vaddr;
2257c478bd9Sstevel@tonic-gate 	uintptr_t end = pmp->pr_vaddr + pmp->pr_size;
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	leak_mtab_t *lm;
2287c478bd9Sstevel@tonic-gate 	pstatus_t *Psp = lmp->lm_pstatus;
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	uintptr_t brk_start = Psp->pr_brkbase;
2317c478bd9Sstevel@tonic-gate 	uintptr_t brk_end = Psp->pr_brkbase + Psp->pr_brksize;
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	int has_brk = 0;
2347c478bd9Sstevel@tonic-gate 	int in_vmem = 0;
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	/*
2377c478bd9Sstevel@tonic-gate 	 * This checks if there is any overlap between the segment and the brk.
2387c478bd9Sstevel@tonic-gate 	 */
2397c478bd9Sstevel@tonic-gate 	if (end > brk_start && start < brk_end)
2407c478bd9Sstevel@tonic-gate 		has_brk = 1;
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	if (leaky_seg_search(start, lmp->lm_segs, lmp->lm_seg_count) != -1)
2437c478bd9Sstevel@tonic-gate 		in_vmem = 1;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	/*
2467c478bd9Sstevel@tonic-gate 	 * We only want anonymous, mmaped memory.  That means:
2477c478bd9Sstevel@tonic-gate 	 *
2487c478bd9Sstevel@tonic-gate 	 * 1. Must be read-write
2497c478bd9Sstevel@tonic-gate 	 * 2. Cannot be shared
2507c478bd9Sstevel@tonic-gate 	 * 3. Cannot have backing
2517c478bd9Sstevel@tonic-gate 	 * 4. Cannot be in the brk
2527c478bd9Sstevel@tonic-gate 	 * 5. Cannot be part of the vmem heap.
2537c478bd9Sstevel@tonic-gate 	 */
2547c478bd9Sstevel@tonic-gate 	if ((pmp->pr_mflags & (MA_READ | MA_WRITE)) == (MA_READ | MA_WRITE) &&
2557c478bd9Sstevel@tonic-gate 	    (pmp->pr_mflags & MA_SHARED) == 0 &&
2567c478bd9Sstevel@tonic-gate 	    (pmp->pr_mapname[0] == 0) &&
2577c478bd9Sstevel@tonic-gate 	    !has_brk &&
2587c478bd9Sstevel@tonic-gate 	    !in_vmem) {
2597c478bd9Sstevel@tonic-gate 		dprintf(("mmaped region: [%p, %p)\n", start, end));
2607c478bd9Sstevel@tonic-gate 		lm = (*lmp->lm_lmp)++;
2617c478bd9Sstevel@tonic-gate 		lm->lkm_base = start;
2627c478bd9Sstevel@tonic-gate 		lm->lkm_limit = end;
2637c478bd9Sstevel@tonic-gate 		lm->lkm_bufctl = LKM_CTL(pmp->pr_vaddr, LKM_CTL_MEMORY);
2647c478bd9Sstevel@tonic-gate 	}
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate static void
leaky_handle_sbrk(leaky_maps_t * lmp)2707c478bd9Sstevel@tonic-gate leaky_handle_sbrk(leaky_maps_t *lmp)
2717c478bd9Sstevel@tonic-gate {
2727c478bd9Sstevel@tonic-gate 	uintptr_t brkbase = lmp->lm_pstatus->pr_brkbase;
2737c478bd9Sstevel@tonic-gate 	uintptr_t brkend = brkbase + lmp->lm_pstatus->pr_brksize;
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 	leak_mtab_t *lm;
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate 	leaky_seg_info_t *segs = lmp->lm_segs;
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 	int x, first = -1, last = -1;
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	dprintf(("brk: [%p, %p)\n", brkbase, brkend));
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 	for (x = 0; x < lmp->lm_seg_count; x++) {
2847c478bd9Sstevel@tonic-gate 		if (segs[x].ls_start >= brkbase && segs[x].ls_end <= brkend) {
2857c478bd9Sstevel@tonic-gate 			if (first == -1)
2867c478bd9Sstevel@tonic-gate 				first = x;
2877c478bd9Sstevel@tonic-gate 			last = x;
2887c478bd9Sstevel@tonic-gate 		}
2897c478bd9Sstevel@tonic-gate 	}
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 	if (brkbase == brkend) {
2927c478bd9Sstevel@tonic-gate 		dprintf(("empty brk -- do nothing\n"));
2937c478bd9Sstevel@tonic-gate 	} else if (first == -1) {
2947c478bd9Sstevel@tonic-gate 		dprintf(("adding [%p, %p) whole brk\n", brkbase, brkend));
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 		lm = (*lmp->lm_lmp)++;
2977c478bd9Sstevel@tonic-gate 		lm->lkm_base = brkbase;
2987c478bd9Sstevel@tonic-gate 		lm->lkm_limit = brkend;
2997c478bd9Sstevel@tonic-gate 		lm->lkm_bufctl = LKM_CTL(brkbase, LKM_CTL_MEMORY);
3007c478bd9Sstevel@tonic-gate 	} else {
3017c478bd9Sstevel@tonic-gate 		uintptr_t curbrk = P2ROUNDUP(brkbase, umem_pagesize);
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 		if (curbrk != segs[first].ls_start) {
3047c478bd9Sstevel@tonic-gate 			dprintf(("adding [%p, %p) in brk, before first seg\n",
3057c478bd9Sstevel@tonic-gate 			    brkbase, segs[first].ls_start));
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 			lm = (*lmp->lm_lmp)++;
3087c478bd9Sstevel@tonic-gate 			lm->lkm_base = brkbase;
3097c478bd9Sstevel@tonic-gate 			lm->lkm_limit = segs[first].ls_start;
3107c478bd9Sstevel@tonic-gate 			lm->lkm_bufctl = LKM_CTL(brkbase, LKM_CTL_MEMORY);
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 			curbrk = segs[first].ls_start;
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 		} else if (curbrk != brkbase) {
3157c478bd9Sstevel@tonic-gate 			dprintf(("ignore [%p, %p) -- realign\n", brkbase,
3167c478bd9Sstevel@tonic-gate 			    curbrk));
3177c478bd9Sstevel@tonic-gate 		}
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 		for (x = first; x <= last; x++) {
3207c478bd9Sstevel@tonic-gate 			if (curbrk < segs[x].ls_start) {
3217c478bd9Sstevel@tonic-gate 				dprintf(("adding [%p, %p) in brk\n", curbrk,
3227c478bd9Sstevel@tonic-gate 				    segs[x].ls_start));
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 				lm = (*lmp->lm_lmp)++;
3257c478bd9Sstevel@tonic-gate 				lm->lkm_base = curbrk;
3267c478bd9Sstevel@tonic-gate 				lm->lkm_limit = segs[x].ls_start;
3277c478bd9Sstevel@tonic-gate 				lm->lkm_bufctl = LKM_CTL(curbrk,
3287c478bd9Sstevel@tonic-gate 				    LKM_CTL_MEMORY);
3297c478bd9Sstevel@tonic-gate 			}
3307c478bd9Sstevel@tonic-gate 			curbrk = segs[x].ls_end;
3317c478bd9Sstevel@tonic-gate 		}
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 		if (curbrk < brkend) {
3347c478bd9Sstevel@tonic-gate 			dprintf(("adding [%p, %p) in brk, after last seg\n",
3357c478bd9Sstevel@tonic-gate 			    curbrk, brkend));
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 			lm = (*lmp->lm_lmp)++;
3387c478bd9Sstevel@tonic-gate 			lm->lkm_base = curbrk;
3397c478bd9Sstevel@tonic-gate 			lm->lkm_limit = brkend;
3407c478bd9Sstevel@tonic-gate 			lm->lkm_bufctl = LKM_CTL(curbrk, LKM_CTL_MEMORY);
3417c478bd9Sstevel@tonic-gate 		}
3427c478bd9Sstevel@tonic-gate 	}
3437c478bd9Sstevel@tonic-gate }
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate static int
leaky_handle_anon_mappings(leak_mtab_t ** lmp)3467c478bd9Sstevel@tonic-gate leaky_handle_anon_mappings(leak_mtab_t **lmp)
3477c478bd9Sstevel@tonic-gate {
3487c478bd9Sstevel@tonic-gate 	leaky_maps_t		lm;
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 	vmem_t *heap_arena;
3517c478bd9Sstevel@tonic-gate 	vmem_t *vm_next;
3527c478bd9Sstevel@tonic-gate 	vmem_t *heap_top;
3537c478bd9Sstevel@tonic-gate 	vmem_t vmem;
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 	pstatus_t Ps;
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	if (mdb_get_xdata("pstatus", &Ps, sizeof (Ps)) == -1) {
3587c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read pstatus xdata");
3597c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
3607c478bd9Sstevel@tonic-gate 	}
3617c478bd9Sstevel@tonic-gate 	lm.lm_pstatus = &Ps;
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 	leak_brkbase = Ps.pr_brkbase;
3647c478bd9Sstevel@tonic-gate 	leak_brksize = Ps.pr_brksize;
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	if (umem_readvar(&heap_arena, "heap_arena") == -1) {
3677c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read heap_arena");
3687c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
3697c478bd9Sstevel@tonic-gate 	}
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	if (heap_arena == NULL) {
3727c478bd9Sstevel@tonic-gate 		mdb_warn("heap_arena is NULL.\n");
3737c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
3747c478bd9Sstevel@tonic-gate 	}
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	for (vm_next = heap_arena; vm_next != NULL; vm_next = vmem.vm_source) {
3777c478bd9Sstevel@tonic-gate 		if (mdb_vread(&vmem, sizeof (vmem), (uintptr_t)vm_next) == -1) {
3787c478bd9Sstevel@tonic-gate 			mdb_warn("couldn't read vmem at %p", vm_next);
3797c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
3807c478bd9Sstevel@tonic-gate 		}
3817c478bd9Sstevel@tonic-gate 		heap_top = vm_next;
3827c478bd9Sstevel@tonic-gate 	}
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	lm.lm_seg_count = 0;
3857c478bd9Sstevel@tonic-gate 	lm.lm_seg_max = 0;
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	if (mdb_pwalk("vmem_span", (mdb_walk_cb_t)leaky_count,
3887c478bd9Sstevel@tonic-gate 	    &lm.lm_seg_max, (uintptr_t)heap_top) == -1) {
3897c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't walk vmem_span for vmem %p", heap_top);
3907c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
3917c478bd9Sstevel@tonic-gate 	}
3927c478bd9Sstevel@tonic-gate 	lm.lm_segs = mdb_alloc(lm.lm_seg_max * sizeof (*lm.lm_segs),
3937c478bd9Sstevel@tonic-gate 	    UM_SLEEP | UM_GC);
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	if (mdb_pwalk("vmem_span", (mdb_walk_cb_t)leaky_read_segs, &lm,
3967c478bd9Sstevel@tonic-gate 	    (uintptr_t)heap_top) == -1) {
3977c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't walk vmem_span for vmem %p",
3987c478bd9Sstevel@tonic-gate 		    heap_top);
3997c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
4007c478bd9Sstevel@tonic-gate 	}
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	if (lm.lm_seg_count > lm.lm_seg_max) {
4037c478bd9Sstevel@tonic-gate 		mdb_warn("segment list for vmem %p grew\n", heap_top);
4047c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
4057c478bd9Sstevel@tonic-gate 	}
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	qsort(lm.lm_segs, lm.lm_seg_count, sizeof (*lm.lm_segs), leaky_seg_cmp);
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 	lm.lm_lmp = lmp;
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	prockludge_add_walkers();
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	if (mdb_walk(KLUDGE_MAPWALK_NAME,
4147c478bd9Sstevel@tonic-gate 	    (mdb_walk_cb_t)leaky_process_anon_mappings, &lm) == -1) {
4157c478bd9Sstevel@tonic-gate 		mdb_warn("Couldn't walk "KLUDGE_MAPWALK_NAME);
4167c478bd9Sstevel@tonic-gate 		prockludge_remove_walkers();
4177c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
4187c478bd9Sstevel@tonic-gate 	}
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 	prockludge_remove_walkers();
4217c478bd9Sstevel@tonic-gate 	leaky_handle_sbrk(&lm);
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
4247c478bd9Sstevel@tonic-gate }
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate static int
leaky_interested(const umem_cache_t * c)4277c478bd9Sstevel@tonic-gate leaky_interested(const umem_cache_t *c)
4287c478bd9Sstevel@tonic-gate {
4297c478bd9Sstevel@tonic-gate 	vmem_t vmem;
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 	if (mdb_vread(&vmem, sizeof (vmem), (uintptr_t)c->cache_arena) == -1) {
4327c478bd9Sstevel@tonic-gate 		mdb_warn("cannot read arena %p for cache '%s'",
4337c478bd9Sstevel@tonic-gate 		    (uintptr_t)c->cache_arena, c->cache_name);
4347c478bd9Sstevel@tonic-gate 		return (0);
4357c478bd9Sstevel@tonic-gate 	}
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate 	/*
4387c478bd9Sstevel@tonic-gate 	 * If this cache isn't allocating from either the umem_default or
4397c478bd9Sstevel@tonic-gate 	 * umem_firewall vmem arena, we're not interested.
4407c478bd9Sstevel@tonic-gate 	 */
4417c478bd9Sstevel@tonic-gate 	if (strcmp(vmem.vm_name, "umem_default") != 0 &&
4427c478bd9Sstevel@tonic-gate 	    strcmp(vmem.vm_name, "umem_firewall") != 0) {
4437c478bd9Sstevel@tonic-gate 		dprintf(("Skipping cache '%s' with arena '%s'\n",
4447c478bd9Sstevel@tonic-gate 		    c->cache_name, vmem.vm_name));
4457c478bd9Sstevel@tonic-gate 		return (0);
4467c478bd9Sstevel@tonic-gate 	}
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 	return (1);
4497c478bd9Sstevel@tonic-gate }
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4527c478bd9Sstevel@tonic-gate static int
leaky_estimate(uintptr_t addr,const umem_cache_t * c,size_t * est)4537c478bd9Sstevel@tonic-gate leaky_estimate(uintptr_t addr, const umem_cache_t *c, size_t *est)
4547c478bd9Sstevel@tonic-gate {
4557c478bd9Sstevel@tonic-gate 	if (!leaky_interested(c))
4567c478bd9Sstevel@tonic-gate 		return (WALK_NEXT);
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	*est += umem_estimate_allocated(addr, c);
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
4617c478bd9Sstevel@tonic-gate }
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4647c478bd9Sstevel@tonic-gate static int
leaky_cache(uintptr_t addr,const umem_cache_t * c,leak_mtab_t ** lmp)4657c478bd9Sstevel@tonic-gate leaky_cache(uintptr_t addr, const umem_cache_t *c, leak_mtab_t **lmp)
4667c478bd9Sstevel@tonic-gate {
4677c478bd9Sstevel@tonic-gate 	leak_mtab_t *lm = *lmp;
4687c478bd9Sstevel@tonic-gate 	mdb_walk_cb_t cb;
4697c478bd9Sstevel@tonic-gate 	const char *walk;
4707c478bd9Sstevel@tonic-gate 	int audit = (c->cache_flags & UMF_AUDIT);
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	if (!leaky_interested(c))
4737c478bd9Sstevel@tonic-gate 		return (WALK_NEXT);
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 	if (audit) {
4767c478bd9Sstevel@tonic-gate 		walk = "bufctl";
4777c478bd9Sstevel@tonic-gate 		cb = (mdb_walk_cb_t)leaky_mtab;
4787c478bd9Sstevel@tonic-gate 	} else {
4797c478bd9Sstevel@tonic-gate 		walk = "umem";
4807c478bd9Sstevel@tonic-gate 		cb = (mdb_walk_cb_t)leaky_mtab_addr;
4817c478bd9Sstevel@tonic-gate 	}
4827c478bd9Sstevel@tonic-gate 	if (mdb_pwalk(walk, cb, lmp, addr) == -1) {
4837c478bd9Sstevel@tonic-gate 		mdb_warn("can't walk umem for cache %p (%s)", addr,
4847c478bd9Sstevel@tonic-gate 		    c->cache_name);
4857c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
4867c478bd9Sstevel@tonic-gate 	}
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 	for (; lm < *lmp; lm++) {
4897c478bd9Sstevel@tonic-gate 		lm->lkm_limit = lm->lkm_base + c->cache_bufsize;
4907c478bd9Sstevel@tonic-gate 		if (!audit)
4917c478bd9Sstevel@tonic-gate 			lm->lkm_bufctl = LKM_CTL(addr, LKM_CTL_CACHE);
4927c478bd9Sstevel@tonic-gate 	}
4937c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
4947c478bd9Sstevel@tonic-gate }
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate static char *map_head = "%-?s  %?s  %-10s used reason\n";
4977c478bd9Sstevel@tonic-gate static char *map_fmt  = "[%?p,%?p) %-10s ";
4987c478bd9Sstevel@tonic-gate #define	BACKING_LEN 10 /* must match the third field's width in map_fmt */
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate static void
leaky_mappings_header(void)5017c478bd9Sstevel@tonic-gate leaky_mappings_header(void)
5027c478bd9Sstevel@tonic-gate {
5037c478bd9Sstevel@tonic-gate 	dprintf((map_head, "mapping", "", "backing"));
5047c478bd9Sstevel@tonic-gate }
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate /* ARGSUSED */
5077c478bd9Sstevel@tonic-gate static int
leaky_grep_mappings(uintptr_t ignored,const prmap_t * pmp,const pstatus_t * Psp)5087c478bd9Sstevel@tonic-gate leaky_grep_mappings(uintptr_t ignored, const prmap_t *pmp,
5097c478bd9Sstevel@tonic-gate     const pstatus_t *Psp)
5107c478bd9Sstevel@tonic-gate {
5117c478bd9Sstevel@tonic-gate 	const char *map_libname_ptr;
5127c478bd9Sstevel@tonic-gate 	char db_mp_name[BACKING_LEN+1];
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 	map_libname_ptr = strrchr(pmp->pr_mapname, '/');
5157c478bd9Sstevel@tonic-gate 	if (map_libname_ptr != NULL)
5167c478bd9Sstevel@tonic-gate 		map_libname_ptr++;
5177c478bd9Sstevel@tonic-gate 	else
5187c478bd9Sstevel@tonic-gate 		map_libname_ptr = pmp->pr_mapname;
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	strlcpy(db_mp_name, map_libname_ptr, sizeof (db_mp_name));
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 	dprintf((map_fmt, pmp->pr_vaddr, (char *)pmp->pr_vaddr + pmp->pr_size,
5237c478bd9Sstevel@tonic-gate 	    db_mp_name));
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate #define	USE(rsn)	dprintf_cont(("yes  %s\n", (rsn)))
5267c478bd9Sstevel@tonic-gate #define	IGNORE(rsn)	dprintf_cont(("no   %s\n", (rsn)))
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	if (!(pmp->pr_mflags & MA_WRITE) || !(pmp->pr_mflags & MA_READ)) {
5297c478bd9Sstevel@tonic-gate 		IGNORE("read-only");
5307c478bd9Sstevel@tonic-gate 	} else if (pmp->pr_vaddr <= Psp->pr_brkbase &&
5317c478bd9Sstevel@tonic-gate 	    pmp->pr_vaddr + pmp->pr_size > Psp->pr_brkbase) {
5327c478bd9Sstevel@tonic-gate 		USE("bss");			/* grab up to brkbase */
5337c478bd9Sstevel@tonic-gate 		leaky_grep(pmp->pr_vaddr, Psp->pr_brkbase - pmp->pr_vaddr);
5347c478bd9Sstevel@tonic-gate 	} else if (pmp->pr_vaddr >= Psp->pr_brkbase &&
5357c478bd9Sstevel@tonic-gate 	    pmp->pr_vaddr < Psp->pr_brkbase + Psp->pr_brksize) {
5367c478bd9Sstevel@tonic-gate 		IGNORE("in brk");
5377c478bd9Sstevel@tonic-gate 	} else if (pmp->pr_vaddr == Psp->pr_stkbase &&
5387c478bd9Sstevel@tonic-gate 	    pmp->pr_size == Psp->pr_stksize) {
5397c478bd9Sstevel@tonic-gate 		IGNORE("stack");
5407c478bd9Sstevel@tonic-gate 	} else if (0 == strcmp(map_libname_ptr, "a.out")) {
5417c478bd9Sstevel@tonic-gate 		USE("a.out data");
5427c478bd9Sstevel@tonic-gate 		leaky_grep(pmp->pr_vaddr, pmp->pr_size);
5437c478bd9Sstevel@tonic-gate 	} else if (0 == strncmp(map_libname_ptr, "libumem.so", 10)) {
5447c478bd9Sstevel@tonic-gate 		IGNORE("part of umem");
5457c478bd9Sstevel@tonic-gate 	} else if (pmp->pr_mapname[0] != 0) {
5467c478bd9Sstevel@tonic-gate 		USE("lib data");		/* library data/bss */
5477c478bd9Sstevel@tonic-gate 		leaky_grep(pmp->pr_vaddr, pmp->pr_size);
5487c478bd9Sstevel@tonic-gate 	} else if ((pmp->pr_mflags & MA_ANON) && pmp->pr_mapname[0] == 0) {
5497c478bd9Sstevel@tonic-gate 		IGNORE("anon");
5507c478bd9Sstevel@tonic-gate 	} else {
5517c478bd9Sstevel@tonic-gate 		IGNORE("");		/* default to ignoring */
5527c478bd9Sstevel@tonic-gate 	}
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate #undef	USE
5557c478bd9Sstevel@tonic-gate #undef	IGNORE
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
5587c478bd9Sstevel@tonic-gate }
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5617c478bd9Sstevel@tonic-gate static int
leaky_mark_lwp(void * ignored,const lwpstatus_t * lwp)5627c478bd9Sstevel@tonic-gate leaky_mark_lwp(void *ignored, const lwpstatus_t *lwp)
5637c478bd9Sstevel@tonic-gate {
5647c478bd9Sstevel@tonic-gate 	leaky_mark_ptr(lwp->pr_reg[R_SP] + STACK_BIAS);
5657c478bd9Sstevel@tonic-gate 	return (0);
5667c478bd9Sstevel@tonic-gate }
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5697c478bd9Sstevel@tonic-gate static int
leaky_process_lwp(void * ignored,const lwpstatus_t * lwp)5707c478bd9Sstevel@tonic-gate leaky_process_lwp(void *ignored, const lwpstatus_t *lwp)
5717c478bd9Sstevel@tonic-gate {
5727c478bd9Sstevel@tonic-gate 	const uintptr_t *regs = (const uintptr_t *)&lwp->pr_reg;
5737c478bd9Sstevel@tonic-gate 	int i;
5747c478bd9Sstevel@tonic-gate 	uintptr_t sp;
5757c478bd9Sstevel@tonic-gate 	uintptr_t addr;
5767c478bd9Sstevel@tonic-gate 	size_t size;
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	for (i = 0; i < R_SP; i++)
5797c478bd9Sstevel@tonic-gate 		leaky_grep_ptr(regs[i]);
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	sp = regs[i++] + STACK_BIAS;
5827c478bd9Sstevel@tonic-gate 	if (leaky_lookup_marked(sp, &addr, &size))
5837c478bd9Sstevel@tonic-gate 		leaky_grep(sp, size - (sp - addr));
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 	for (; i < NPRGREG; i++)
5867c478bd9Sstevel@tonic-gate 		leaky_grep_ptr(regs[i]);
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	return (0);
5897c478bd9Sstevel@tonic-gate }
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate /*
5927c478bd9Sstevel@tonic-gate  * Handles processing various proc-related things:
5937c478bd9Sstevel@tonic-gate  * 1. calls leaky_process_lwp on each the LWP
5947c478bd9Sstevel@tonic-gate  * 2. leaky_greps the bss/data of libraries and a.out, and the a.out stack.
5957c478bd9Sstevel@tonic-gate  */
5967c478bd9Sstevel@tonic-gate static int
leaky_process_proc(void)5977c478bd9Sstevel@tonic-gate leaky_process_proc(void)
5987c478bd9Sstevel@tonic-gate {
5997c478bd9Sstevel@tonic-gate 	pstatus_t Ps;
6007c478bd9Sstevel@tonic-gate 	struct ps_prochandle *Pr;
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 	if (mdb_get_xdata("pstatus", &Ps, sizeof (Ps)) == -1) {
6037c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read pstatus xdata");
6047c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6057c478bd9Sstevel@tonic-gate 	}
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	dprintf(("pstatus says:\n"));
6087c478bd9Sstevel@tonic-gate 	dprintf(("\tbrk: base %p size %p\n",
6097c478bd9Sstevel@tonic-gate 	    Ps.pr_brkbase, Ps.pr_brksize));
6107c478bd9Sstevel@tonic-gate 	dprintf(("\tstk: base %p size %p\n",
6117c478bd9Sstevel@tonic-gate 	    Ps.pr_stkbase, Ps.pr_stksize));
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 	if (mdb_get_xdata("pshandle", &Pr, sizeof (Pr)) == -1) {
6147c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read pshandle xdata");
6157c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6167c478bd9Sstevel@tonic-gate 	}
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 	if (Plwp_iter(Pr, leaky_mark_lwp, NULL) != 0) {
6197c478bd9Sstevel@tonic-gate 		mdb_warn("findleaks: Failed to iterate lwps\n");
6207c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6217c478bd9Sstevel@tonic-gate 	}
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	if (Plwp_iter(Pr, leaky_process_lwp, NULL) != 0) {
6247c478bd9Sstevel@tonic-gate 		mdb_warn("findleaks: Failed to iterate lwps\n");
6257c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6267c478bd9Sstevel@tonic-gate 	}
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	prockludge_add_walkers();
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate 	leaky_mappings_header();
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate 	if (mdb_walk(KLUDGE_MAPWALK_NAME, (mdb_walk_cb_t)leaky_grep_mappings,
6337c478bd9Sstevel@tonic-gate 	    &Ps) == -1) {
6347c478bd9Sstevel@tonic-gate 		mdb_warn("Couldn't walk "KLUDGE_MAPWALK_NAME);
6357c478bd9Sstevel@tonic-gate 		prockludge_remove_walkers();
6367c478bd9Sstevel@tonic-gate 		return (-1);
6377c478bd9Sstevel@tonic-gate 	}
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 	prockludge_remove_walkers();
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	return (0);
6427c478bd9Sstevel@tonic-gate }
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate static void
leaky_subr_caller(const uintptr_t * stack,uint_t depth,char * buf,uintptr_t * pcp)6457c478bd9Sstevel@tonic-gate leaky_subr_caller(const uintptr_t *stack, uint_t depth, char *buf,
6467c478bd9Sstevel@tonic-gate     uintptr_t *pcp)
6477c478bd9Sstevel@tonic-gate {
6487c478bd9Sstevel@tonic-gate 	int i;
6497c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
6507c478bd9Sstevel@tonic-gate 	uintptr_t pc = 0;
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 	buf[0] = 0;
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	for (i = 0; i < depth; i++) {
6557c478bd9Sstevel@tonic-gate 		pc = stack[i];
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 		if (mdb_lookup_by_addr(pc,
6587c478bd9Sstevel@tonic-gate 		    MDB_SYM_FUZZY, buf, MDB_SYM_NAMLEN, &sym) == -1)
6597c478bd9Sstevel@tonic-gate 			continue;
6607c478bd9Sstevel@tonic-gate 		if (strncmp(buf, "libumem.so", 10) == 0)
6617c478bd9Sstevel@tonic-gate 			continue;
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 		*pcp = pc;
6647c478bd9Sstevel@tonic-gate 		return;
6657c478bd9Sstevel@tonic-gate 	}
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	/*
6687c478bd9Sstevel@tonic-gate 	 * We're only here if the entire call chain is in libumem.so;
6697c478bd9Sstevel@tonic-gate 	 * this shouldn't happen, but we'll just use the last caller.
6707c478bd9Sstevel@tonic-gate 	 */
6717c478bd9Sstevel@tonic-gate 	*pcp = pc;
6727c478bd9Sstevel@tonic-gate }
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate int
leaky_subr_bufctl_cmp(const leak_bufctl_t * lhs,const leak_bufctl_t * rhs)6757c478bd9Sstevel@tonic-gate leaky_subr_bufctl_cmp(const leak_bufctl_t *lhs, const leak_bufctl_t *rhs)
6767c478bd9Sstevel@tonic-gate {
6777c478bd9Sstevel@tonic-gate 	char lbuf[MDB_SYM_NAMLEN], rbuf[MDB_SYM_NAMLEN];
6787c478bd9Sstevel@tonic-gate 	uintptr_t lcaller, rcaller;
6797c478bd9Sstevel@tonic-gate 	int rval;
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	leaky_subr_caller(lhs->lkb_stack, lhs->lkb_depth, lbuf, &lcaller);
6827c478bd9Sstevel@tonic-gate 	leaky_subr_caller(rhs->lkb_stack, lhs->lkb_depth, rbuf, &rcaller);
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	if (rval = strcmp(lbuf, rbuf))
6857c478bd9Sstevel@tonic-gate 		return (rval);
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 	if (lcaller < rcaller)
6887c478bd9Sstevel@tonic-gate 		return (-1);
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	if (lcaller > rcaller)
6917c478bd9Sstevel@tonic-gate 		return (1);
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	if (lhs->lkb_data < rhs->lkb_data)
6947c478bd9Sstevel@tonic-gate 		return (-1);
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	if (lhs->lkb_data > rhs->lkb_data)
6977c478bd9Sstevel@tonic-gate 		return (1);
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	return (0);
7007c478bd9Sstevel@tonic-gate }
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7037c478bd9Sstevel@tonic-gate int
leaky_subr_estimate(size_t * estp)7047c478bd9Sstevel@tonic-gate leaky_subr_estimate(size_t *estp)
7057c478bd9Sstevel@tonic-gate {
706*789d94c2Sjwadams 	if (umem_ready == 0) {
707*789d94c2Sjwadams 		mdb_warn(
708*789d94c2Sjwadams 		    "findleaks: umem is not loaded in the address space\n");
709*789d94c2Sjwadams 		return (DCMD_ERR);
710*789d94c2Sjwadams 	}
7117c478bd9Sstevel@tonic-gate 
712*789d94c2Sjwadams 	if (umem_ready == UMEM_READY_INIT_FAILED) {
713*789d94c2Sjwadams 		mdb_warn("findleaks: umem initialization failed -- no "
714*789d94c2Sjwadams 		    "possible leaks.\n");
7157c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7167c478bd9Sstevel@tonic-gate 	}
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 	if (umem_ready != UMEM_READY) {
7197c478bd9Sstevel@tonic-gate 		mdb_warn("findleaks: No allocations have occured -- no "
7207c478bd9Sstevel@tonic-gate 		    "possible leaks.\n");
7217c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7227c478bd9Sstevel@tonic-gate 	}
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	if (mdb_walk("umem_cache", (mdb_walk_cb_t)leaky_estimate, estp) == -1) {
7257c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't walk 'umem_cache'");
7267c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7277c478bd9Sstevel@tonic-gate 	}
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 	if (mdb_walk("vmem", (mdb_walk_cb_t)leaky_estimate_vmem, estp) == -1) {
7307c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't walk 'vmem'");
7317c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7327c478bd9Sstevel@tonic-gate 	}
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	if (*estp == 0) {
7357c478bd9Sstevel@tonic-gate 		mdb_warn("findleaks: No allocated buffers found.\n");
7367c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7377c478bd9Sstevel@tonic-gate 	}
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 	prockludge_add_walkers();
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	if (mdb_walk(KLUDGE_MAPWALK_NAME, (mdb_walk_cb_t)leaky_count,
7427c478bd9Sstevel@tonic-gate 	    estp) == -1) {
7437c478bd9Sstevel@tonic-gate 		mdb_warn("Couldn't walk "KLUDGE_MAPWALK_NAME);
7447c478bd9Sstevel@tonic-gate 		prockludge_remove_walkers();
7457c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7467c478bd9Sstevel@tonic-gate 	}
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	prockludge_remove_walkers();
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
7517c478bd9Sstevel@tonic-gate }
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate int
leaky_subr_fill(leak_mtab_t ** lmpp)7547c478bd9Sstevel@tonic-gate leaky_subr_fill(leak_mtab_t **lmpp)
7557c478bd9Sstevel@tonic-gate {
7567c478bd9Sstevel@tonic-gate 	if (leaky_handle_anon_mappings(lmpp) != DCMD_OK) {
7577c478bd9Sstevel@tonic-gate 		mdb_warn("unable to process mappings\n");
7587c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7597c478bd9Sstevel@tonic-gate 	}
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 	if (mdb_walk("vmem", (mdb_walk_cb_t)leaky_vmem, lmpp) == -1) {
7627c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't walk 'vmem'");
7637c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	if (mdb_walk("umem_cache", (mdb_walk_cb_t)leaky_cache, lmpp) == -1) {
7677c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't walk 'umem_cache'");
7687c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7697c478bd9Sstevel@tonic-gate 	}
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
7727c478bd9Sstevel@tonic-gate }
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate int
leaky_subr_run(void)7757c478bd9Sstevel@tonic-gate leaky_subr_run(void)
7767c478bd9Sstevel@tonic-gate {
7777c478bd9Sstevel@tonic-gate 	if (leaky_process_proc() == DCMD_ERR) {
7787c478bd9Sstevel@tonic-gate 		mdb_warn("failed to process proc");
7797c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7807c478bd9Sstevel@tonic-gate 	}
7817c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
7827c478bd9Sstevel@tonic-gate }
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate void
leaky_subr_add_leak(leak_mtab_t * lmp)7857c478bd9Sstevel@tonic-gate leaky_subr_add_leak(leak_mtab_t *lmp)
7867c478bd9Sstevel@tonic-gate {
7877c478bd9Sstevel@tonic-gate 	uintptr_t addr = LKM_CTLPTR(lmp->lkm_bufctl);
7887c478bd9Sstevel@tonic-gate 	uint_t depth;
7897c478bd9Sstevel@tonic-gate 
7907c478bd9Sstevel@tonic-gate 	vmem_seg_t vs;
7917c478bd9Sstevel@tonic-gate 	umem_bufctl_audit_t *bcp;
7927c478bd9Sstevel@tonic-gate 	UMEM_LOCAL_BUFCTL_AUDIT(&bcp);
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	switch (LKM_CTLTYPE(lmp->lkm_bufctl)) {
7957c478bd9Sstevel@tonic-gate 	case LKM_CTL_BUFCTL:
7967c478bd9Sstevel@tonic-gate 		if (mdb_vread(bcp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) {
7977c478bd9Sstevel@tonic-gate 			mdb_warn("couldn't read leaked bufctl at addr %p",
7987c478bd9Sstevel@tonic-gate 			    addr);
7997c478bd9Sstevel@tonic-gate 			return;
8007c478bd9Sstevel@tonic-gate 		}
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 		depth = MIN(bcp->bc_depth, umem_stack_depth);
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 		/*
8057c478bd9Sstevel@tonic-gate 		 * The top of the stack will be in umem_cache_alloc().
8067c478bd9Sstevel@tonic-gate 		 * Since the offset in umem_cache_alloc() isn't interesting
8077c478bd9Sstevel@tonic-gate 		 * we skip that frame for the purposes of uniquifying stacks.
8087c478bd9Sstevel@tonic-gate 		 *
8097c478bd9Sstevel@tonic-gate 		 * Also, we use the cache pointer as the leaks's cid, to
8107c478bd9Sstevel@tonic-gate 		 * prevent the coalescing of leaks from different caches.
8117c478bd9Sstevel@tonic-gate 		 */
8127c478bd9Sstevel@tonic-gate 		if (depth > 0)
8137c478bd9Sstevel@tonic-gate 			depth--;
8147c478bd9Sstevel@tonic-gate 		leaky_add_leak(TYPE_UMEM, addr, (uintptr_t)bcp->bc_addr,
8157c478bd9Sstevel@tonic-gate 		    bcp->bc_timestamp, bcp->bc_stack + 1, depth,
8167c478bd9Sstevel@tonic-gate 		    (uintptr_t)bcp->bc_cache, (uintptr_t)bcp->bc_cache);
8177c478bd9Sstevel@tonic-gate 		break;
8187c478bd9Sstevel@tonic-gate 	case LKM_CTL_VMSEG:
8197c478bd9Sstevel@tonic-gate 		if (mdb_vread(&vs, sizeof (vs), addr) == -1) {
8207c478bd9Sstevel@tonic-gate 			mdb_warn("couldn't read leaked vmem_seg at addr %p",
8217c478bd9Sstevel@tonic-gate 			    addr);
8227c478bd9Sstevel@tonic-gate 			return;
8237c478bd9Sstevel@tonic-gate 		}
8247c478bd9Sstevel@tonic-gate 		depth = MIN(vs.vs_depth, VMEM_STACK_DEPTH);
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 		leaky_add_leak(TYPE_VMEM, addr, vs.vs_start, vs.vs_timestamp,
8277c478bd9Sstevel@tonic-gate 		    vs.vs_stack, depth, 0, (vs.vs_end - vs.vs_start));
8287c478bd9Sstevel@tonic-gate 		break;
8297c478bd9Sstevel@tonic-gate 	case LKM_CTL_MEMORY:
8307c478bd9Sstevel@tonic-gate 		if (LEAKY_INBRK(addr))
8317c478bd9Sstevel@tonic-gate 			leaky_add_leak(TYPE_SBRK, addr, addr, 0, NULL, 0, 0,
8327c478bd9Sstevel@tonic-gate 			    lmp->lkm_limit - addr);
8337c478bd9Sstevel@tonic-gate 		else
8347c478bd9Sstevel@tonic-gate 			leaky_add_leak(TYPE_MMAP, addr, addr, 0, NULL, 0, 0,
8357c478bd9Sstevel@tonic-gate 			    lmp->lkm_limit - addr);
8367c478bd9Sstevel@tonic-gate 		break;
8377c478bd9Sstevel@tonic-gate 	case LKM_CTL_CACHE:
8387c478bd9Sstevel@tonic-gate 		leaky_add_leak(TYPE_CACHE, lmp->lkm_base, lmp->lkm_base, 0,
8397c478bd9Sstevel@tonic-gate 		    NULL, 0, addr, addr);
8407c478bd9Sstevel@tonic-gate 		break;
8417c478bd9Sstevel@tonic-gate 	default:
8427c478bd9Sstevel@tonic-gate 		mdb_warn("internal error:  invalid leak_bufctl_t\n");
8437c478bd9Sstevel@tonic-gate 		break;
8447c478bd9Sstevel@tonic-gate 	}
8457c478bd9Sstevel@tonic-gate }
8467c478bd9Sstevel@tonic-gate 
8477c478bd9Sstevel@tonic-gate static int lk_vmem_seen;
8487c478bd9Sstevel@tonic-gate static int lk_cache_seen;
8497c478bd9Sstevel@tonic-gate static int lk_umem_seen;
8507c478bd9Sstevel@tonic-gate static size_t lk_ttl;
8517c478bd9Sstevel@tonic-gate static size_t lk_bytes;
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate void
leaky_subr_dump_start(int type)8547c478bd9Sstevel@tonic-gate leaky_subr_dump_start(int type)
8557c478bd9Sstevel@tonic-gate {
8567c478bd9Sstevel@tonic-gate 	switch (type) {
8577c478bd9Sstevel@tonic-gate 	case TYPE_MMAP:
8587c478bd9Sstevel@tonic-gate 		lk_vmem_seen = 0;
8597c478bd9Sstevel@tonic-gate 		break;
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate 	case TYPE_SBRK:
8627c478bd9Sstevel@tonic-gate 	case TYPE_VMEM:
8637c478bd9Sstevel@tonic-gate 		return;			/* don't zero counts */
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 	case TYPE_CACHE:
8667c478bd9Sstevel@tonic-gate 		lk_cache_seen = 0;
8677c478bd9Sstevel@tonic-gate 		break;
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 	case TYPE_UMEM:
8707c478bd9Sstevel@tonic-gate 		lk_umem_seen = 0;
8717c478bd9Sstevel@tonic-gate 		break;
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	default:
8747c478bd9Sstevel@tonic-gate 		break;
8757c478bd9Sstevel@tonic-gate 	}
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate 	lk_ttl = 0;
8787c478bd9Sstevel@tonic-gate 	lk_bytes = 0;
8797c478bd9Sstevel@tonic-gate }
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate void
leaky_subr_dump(const leak_bufctl_t * lkb,int verbose)8827c478bd9Sstevel@tonic-gate leaky_subr_dump(const leak_bufctl_t *lkb, int verbose)
8837c478bd9Sstevel@tonic-gate {
8847c478bd9Sstevel@tonic-gate 	const leak_bufctl_t *cur;
8857c478bd9Sstevel@tonic-gate 	umem_cache_t cache;
8867c478bd9Sstevel@tonic-gate 	size_t min, max, size;
8877c478bd9Sstevel@tonic-gate 	char sz[30];
8887c478bd9Sstevel@tonic-gate 	char c[MDB_SYM_NAMLEN];
8897c478bd9Sstevel@tonic-gate 	uintptr_t caller;
8907c478bd9Sstevel@tonic-gate 	const char *nm, *nm_lc;
8917c478bd9Sstevel@tonic-gate 	uint8_t type = lkb->lkb_type;
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 	if (verbose) {
8947c478bd9Sstevel@tonic-gate 		lk_ttl = 0;
8957c478bd9Sstevel@tonic-gate 		lk_bytes = 0;
8967c478bd9Sstevel@tonic-gate 	} else if (!lk_vmem_seen && (type == TYPE_VMEM || type == TYPE_MMAP ||
8977c478bd9Sstevel@tonic-gate 	    type == TYPE_SBRK)) {
8987c478bd9Sstevel@tonic-gate 		lk_vmem_seen = 1;
8997c478bd9Sstevel@tonic-gate 		mdb_printf("%-16s %7s %?s %s\n",
9007c478bd9Sstevel@tonic-gate 		    "BYTES", "LEAKED", "VMEM_SEG", "CALLER");
9017c478bd9Sstevel@tonic-gate 	}
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	switch (lkb->lkb_type) {
9047c478bd9Sstevel@tonic-gate 	case TYPE_MMAP:
9057c478bd9Sstevel@tonic-gate 	case TYPE_SBRK:
9067c478bd9Sstevel@tonic-gate 		nm = (lkb->lkb_type == TYPE_MMAP) ? "MMAP" : "SBRK";
9077c478bd9Sstevel@tonic-gate 		nm_lc = (lkb->lkb_type == TYPE_MMAP) ? "mmap(2)" : "sbrk(2)";
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 		for (; lkb != NULL; lkb = lkb->lkb_next) {
9107c478bd9Sstevel@tonic-gate 			if (!verbose)
9117c478bd9Sstevel@tonic-gate 				mdb_printf("%-16d %7d %?p %s\n", lkb->lkb_data,
9127c478bd9Sstevel@tonic-gate 				    lkb->lkb_dups + 1, lkb->lkb_addr, nm);
9137c478bd9Sstevel@tonic-gate 			else
9147c478bd9Sstevel@tonic-gate 				mdb_printf("%s leak: [%p, %p), %ld bytes\n",
9157c478bd9Sstevel@tonic-gate 				    nm_lc, lkb->lkb_addr,
9167c478bd9Sstevel@tonic-gate 				    lkb->lkb_addr + lkb->lkb_data,
9177c478bd9Sstevel@tonic-gate 				    lkb->lkb_data);
9187c478bd9Sstevel@tonic-gate 			lk_ttl++;
9197c478bd9Sstevel@tonic-gate 			lk_bytes += lkb->lkb_data;
9207c478bd9Sstevel@tonic-gate 		}
9217c478bd9Sstevel@tonic-gate 		return;
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 	case TYPE_VMEM:
9247c478bd9Sstevel@tonic-gate 		min = max = lkb->lkb_data;
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 		for (cur = lkb; cur != NULL; cur = cur->lkb_next) {
9277c478bd9Sstevel@tonic-gate 			size = cur->lkb_data;
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 			if (size < min)
9307c478bd9Sstevel@tonic-gate 				min = size;
9317c478bd9Sstevel@tonic-gate 			if (size > max)
9327c478bd9Sstevel@tonic-gate 				max = size;
9337c478bd9Sstevel@tonic-gate 
9347c478bd9Sstevel@tonic-gate 			lk_ttl++;
9357c478bd9Sstevel@tonic-gate 			lk_bytes += size;
9367c478bd9Sstevel@tonic-gate 		}
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 		if (min == max)
9397c478bd9Sstevel@tonic-gate 			(void) mdb_snprintf(sz, sizeof (sz), "%ld", min);
9407c478bd9Sstevel@tonic-gate 		else
9417c478bd9Sstevel@tonic-gate 			(void) mdb_snprintf(sz, sizeof (sz), "%ld-%ld",
9427c478bd9Sstevel@tonic-gate 			    min, max);
9437c478bd9Sstevel@tonic-gate 
9447c478bd9Sstevel@tonic-gate 		if (!verbose) {
9457c478bd9Sstevel@tonic-gate 			leaky_subr_caller(lkb->lkb_stack, lkb->lkb_depth,
9467c478bd9Sstevel@tonic-gate 			    c, &caller);
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 			mdb_printf("%-16s %7d %?p %a\n", sz, lkb->lkb_dups + 1,
9497c478bd9Sstevel@tonic-gate 			    lkb->lkb_addr, caller);
9507c478bd9Sstevel@tonic-gate 		} else {
9517c478bd9Sstevel@tonic-gate 			mdb_arg_t v;
9527c478bd9Sstevel@tonic-gate 
9537c478bd9Sstevel@tonic-gate 			if (lk_ttl == 1)
9547c478bd9Sstevel@tonic-gate 				mdb_printf("umem_oversize leak: 1 vmem_seg, "
9557c478bd9Sstevel@tonic-gate 				    "%ld bytes\n", lk_bytes);
9567c478bd9Sstevel@tonic-gate 			else
9577c478bd9Sstevel@tonic-gate 				mdb_printf("umem_oversize leak: %d vmem_segs, "
9587c478bd9Sstevel@tonic-gate 				    "%s bytes each, %ld bytes total\n",
9597c478bd9Sstevel@tonic-gate 				    lk_ttl, sz, lk_bytes);
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 			v.a_type = MDB_TYPE_STRING;
9627c478bd9Sstevel@tonic-gate 			v.a_un.a_str = "-v";
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 			if (mdb_call_dcmd("vmem_seg", lkb->lkb_addr,
9657c478bd9Sstevel@tonic-gate 			    DCMD_ADDRSPEC, 1, &v) == -1) {
9667c478bd9Sstevel@tonic-gate 				mdb_warn("'%p::vmem_seg -v' failed",
9677c478bd9Sstevel@tonic-gate 				    lkb->lkb_addr);
9687c478bd9Sstevel@tonic-gate 			}
9697c478bd9Sstevel@tonic-gate 		}
9707c478bd9Sstevel@tonic-gate 		return;
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 	case TYPE_CACHE:
9737c478bd9Sstevel@tonic-gate 		if (!lk_cache_seen) {
9747c478bd9Sstevel@tonic-gate 			lk_cache_seen = 1;
9757c478bd9Sstevel@tonic-gate 			if (lk_vmem_seen)
9767c478bd9Sstevel@tonic-gate 				mdb_printf("\n");
9777c478bd9Sstevel@tonic-gate 			mdb_printf("%-?s %7s %?s %s\n",
9787c478bd9Sstevel@tonic-gate 			    "CACHE", "LEAKED", "BUFFER", "CALLER");
9797c478bd9Sstevel@tonic-gate 		}
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate 		if (mdb_vread(&cache, sizeof (cache), lkb->lkb_data) == -1) {
9827c478bd9Sstevel@tonic-gate 			/*
9837c478bd9Sstevel@tonic-gate 			 * This _really_ shouldn't happen; we shouldn't
9847c478bd9Sstevel@tonic-gate 			 * have been able to get this far if this
9857c478bd9Sstevel@tonic-gate 			 * cache wasn't readable.
9867c478bd9Sstevel@tonic-gate 			 */
9877c478bd9Sstevel@tonic-gate 			mdb_warn("can't read cache %p for leaked "
9887c478bd9Sstevel@tonic-gate 			    "buffer %p", lkb->lkb_data, lkb->lkb_addr);
9897c478bd9Sstevel@tonic-gate 			return;
9907c478bd9Sstevel@tonic-gate 		}
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 		lk_ttl += lkb->lkb_dups + 1;
9937c478bd9Sstevel@tonic-gate 		lk_bytes += (lkb->lkb_dups + 1) * cache.cache_bufsize;
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 		caller = (lkb->lkb_depth == 0) ? 0 : lkb->lkb_stack[0];
9967c478bd9Sstevel@tonic-gate 		if (caller != 0) {
9977c478bd9Sstevel@tonic-gate 			(void) mdb_snprintf(c, sizeof (c), "%a", caller);
9987c478bd9Sstevel@tonic-gate 		} else {
9997c478bd9Sstevel@tonic-gate 			(void) mdb_snprintf(c, sizeof (c), "%s",
10007c478bd9Sstevel@tonic-gate 			    (verbose) ? "" : "?");
10017c478bd9Sstevel@tonic-gate 		}
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 		if (!verbose) {
10047c478bd9Sstevel@tonic-gate 			mdb_printf("%0?p %7d %0?p %s\n", lkb->lkb_cid,
10057c478bd9Sstevel@tonic-gate 			    lkb->lkb_dups + 1, lkb->lkb_addr, c);
10067c478bd9Sstevel@tonic-gate 		} else {
10077c478bd9Sstevel@tonic-gate 			if (lk_ttl == 1)
10087c478bd9Sstevel@tonic-gate 				mdb_printf("%s leak: 1 buffer, %ld bytes,\n",
10097c478bd9Sstevel@tonic-gate 				    cache.cache_name, lk_bytes);
10107c478bd9Sstevel@tonic-gate 			else
10117c478bd9Sstevel@tonic-gate 				mdb_printf("%s leak: %d buffers, "
10127c478bd9Sstevel@tonic-gate 				    "%ld bytes each, %ld bytes total,\n",
10137c478bd9Sstevel@tonic-gate 				    cache.cache_name, lk_ttl,
10147c478bd9Sstevel@tonic-gate 				    cache.cache_bufsize, lk_bytes);
10157c478bd9Sstevel@tonic-gate 			mdb_printf("    %s%s%ssample addr %p\n",
10167c478bd9Sstevel@tonic-gate 			    (caller == 0) ? "" : "caller ", c,
10177c478bd9Sstevel@tonic-gate 			    (caller == 0) ? "" : ", ", lkb->lkb_addr);
10187c478bd9Sstevel@tonic-gate 		}
10197c478bd9Sstevel@tonic-gate 		return;
10207c478bd9Sstevel@tonic-gate 
10217c478bd9Sstevel@tonic-gate 	case TYPE_UMEM:
10227c478bd9Sstevel@tonic-gate 		if (!lk_umem_seen) {
10237c478bd9Sstevel@tonic-gate 			lk_umem_seen = 1;
10247c478bd9Sstevel@tonic-gate 			if (lk_vmem_seen || lk_cache_seen)
10257c478bd9Sstevel@tonic-gate 				mdb_printf("\n");
10267c478bd9Sstevel@tonic-gate 			mdb_printf("%-?s %7s %?s %s\n",
10277c478bd9Sstevel@tonic-gate 			    "CACHE", "LEAKED", "BUFCTL", "CALLER");
10287c478bd9Sstevel@tonic-gate 		}
10297c478bd9Sstevel@tonic-gate 		if (mdb_vread(&cache, sizeof (cache), lkb->lkb_data) == -1) {
10307c478bd9Sstevel@tonic-gate 			/*
10317c478bd9Sstevel@tonic-gate 			 * This _really_ shouldn't happen; we shouldn't
10327c478bd9Sstevel@tonic-gate 			 * have been able to get this far if this
10337c478bd9Sstevel@tonic-gate 			 * cache wasn't readable.
10347c478bd9Sstevel@tonic-gate 			 */
10357c478bd9Sstevel@tonic-gate 			mdb_warn("can't read cache %p for leaked "
10367c478bd9Sstevel@tonic-gate 			    "bufctl %p", lkb->lkb_data, lkb->lkb_addr);
10377c478bd9Sstevel@tonic-gate 			return;
10387c478bd9Sstevel@tonic-gate 		}
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 		lk_ttl += lkb->lkb_dups + 1;
10417c478bd9Sstevel@tonic-gate 		lk_bytes += (lkb->lkb_dups + 1) * cache.cache_bufsize;
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate 		if (!verbose) {
10447c478bd9Sstevel@tonic-gate 			leaky_subr_caller(lkb->lkb_stack, lkb->lkb_depth, c,
10457c478bd9Sstevel@tonic-gate 			    &caller);
10467c478bd9Sstevel@tonic-gate 
10477c478bd9Sstevel@tonic-gate 			mdb_printf("%0?p %7d %0?p %a\n", lkb->lkb_data,
10487c478bd9Sstevel@tonic-gate 			    lkb->lkb_dups + 1, lkb->lkb_addr, caller);
10497c478bd9Sstevel@tonic-gate 		} else {
10507c478bd9Sstevel@tonic-gate 			mdb_arg_t v;
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 			if (lk_ttl == 1)
10537c478bd9Sstevel@tonic-gate 				mdb_printf("%s leak: 1 buffer, %ld bytes\n",
10547c478bd9Sstevel@tonic-gate 				    cache.cache_name, lk_bytes);
10557c478bd9Sstevel@tonic-gate 			else
10567c478bd9Sstevel@tonic-gate 				mdb_printf("%s leak: %d buffers, "
10577c478bd9Sstevel@tonic-gate 				    "%ld bytes each, %ld bytes total\n",
10587c478bd9Sstevel@tonic-gate 				    cache.cache_name, lk_ttl,
10597c478bd9Sstevel@tonic-gate 				    cache.cache_bufsize, lk_bytes);
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate 			v.a_type = MDB_TYPE_STRING;
10627c478bd9Sstevel@tonic-gate 			v.a_un.a_str = "-v";
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 			if (mdb_call_dcmd("bufctl", lkb->lkb_addr,
10657c478bd9Sstevel@tonic-gate 			    DCMD_ADDRSPEC, 1, &v) == -1) {
10667c478bd9Sstevel@tonic-gate 				mdb_warn("'%p::bufctl -v' failed",
10677c478bd9Sstevel@tonic-gate 				    lkb->lkb_addr);
10687c478bd9Sstevel@tonic-gate 			}
10697c478bd9Sstevel@tonic-gate 		}
10707c478bd9Sstevel@tonic-gate 		return;
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 	default:
10737c478bd9Sstevel@tonic-gate 		return;
10747c478bd9Sstevel@tonic-gate 	}
10757c478bd9Sstevel@tonic-gate }
10767c478bd9Sstevel@tonic-gate 
10777c478bd9Sstevel@tonic-gate void
leaky_subr_dump_end(int type)10787c478bd9Sstevel@tonic-gate leaky_subr_dump_end(int type)
10797c478bd9Sstevel@tonic-gate {
10807c478bd9Sstevel@tonic-gate 	int i;
10817c478bd9Sstevel@tonic-gate 	int width;
10827c478bd9Sstevel@tonic-gate 	const char *leak;
10837c478bd9Sstevel@tonic-gate 
10847c478bd9Sstevel@tonic-gate 	switch (type) {
10857c478bd9Sstevel@tonic-gate 	case TYPE_VMEM:
10867c478bd9Sstevel@tonic-gate 		if (!lk_vmem_seen)
10877c478bd9Sstevel@tonic-gate 			return;
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate 		width = 16;
10907c478bd9Sstevel@tonic-gate 		leak = "oversized leak";
10917c478bd9Sstevel@tonic-gate 		break;
10927c478bd9Sstevel@tonic-gate 
10937c478bd9Sstevel@tonic-gate 	case TYPE_CACHE:
10947c478bd9Sstevel@tonic-gate 		if (!lk_cache_seen)
10957c478bd9Sstevel@tonic-gate 			return;
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 		width = sizeof (uintptr_t) * 2;
10987c478bd9Sstevel@tonic-gate 		leak = "buffer";
10997c478bd9Sstevel@tonic-gate 		break;
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 	case TYPE_UMEM:
11027c478bd9Sstevel@tonic-gate 		if (!lk_umem_seen)
11037c478bd9Sstevel@tonic-gate 			return;
11047c478bd9Sstevel@tonic-gate 
11057c478bd9Sstevel@tonic-gate 		width = sizeof (uintptr_t) * 2;
11067c478bd9Sstevel@tonic-gate 		leak = "buffer";
11077c478bd9Sstevel@tonic-gate 		break;
11087c478bd9Sstevel@tonic-gate 
11097c478bd9Sstevel@tonic-gate 	default:
11107c478bd9Sstevel@tonic-gate 		return;
11117c478bd9Sstevel@tonic-gate 	}
11127c478bd9Sstevel@tonic-gate 
11137c478bd9Sstevel@tonic-gate 	for (i = 0; i < 72; i++)
11147c478bd9Sstevel@tonic-gate 		mdb_printf("-");
11157c478bd9Sstevel@tonic-gate 	mdb_printf("\n%*s %7ld %s%s, %ld byte%s\n",
11167c478bd9Sstevel@tonic-gate 	    width, "Total", lk_ttl, leak, (lk_ttl == 1) ? "" : "s",
11177c478bd9Sstevel@tonic-gate 	    lk_bytes, (lk_bytes == 1) ? "" : "s");
11187c478bd9Sstevel@tonic-gate }
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate int
leaky_subr_invoke_callback(const leak_bufctl_t * lkb,mdb_walk_cb_t cb,void * cbdata)11217c478bd9Sstevel@tonic-gate leaky_subr_invoke_callback(const leak_bufctl_t *lkb, mdb_walk_cb_t cb,
11227c478bd9Sstevel@tonic-gate     void *cbdata)
11237c478bd9Sstevel@tonic-gate {
11247c478bd9Sstevel@tonic-gate 	vmem_seg_t vs;
11257c478bd9Sstevel@tonic-gate 	umem_bufctl_audit_t *bcp;
11267c478bd9Sstevel@tonic-gate 	UMEM_LOCAL_BUFCTL_AUDIT(&bcp);
11277c478bd9Sstevel@tonic-gate 
11287c478bd9Sstevel@tonic-gate 	switch (lkb->lkb_type) {
11297c478bd9Sstevel@tonic-gate 	case TYPE_VMEM:
11307c478bd9Sstevel@tonic-gate 		if (mdb_vread(&vs, sizeof (vs), lkb->lkb_addr) == -1) {
11317c478bd9Sstevel@tonic-gate 			mdb_warn("unable to read vmem_seg at %p",
11327c478bd9Sstevel@tonic-gate 			    lkb->lkb_addr);
11337c478bd9Sstevel@tonic-gate 			return (WALK_NEXT);
11347c478bd9Sstevel@tonic-gate 		}
11357c478bd9Sstevel@tonic-gate 		return (cb(lkb->lkb_addr, &vs, cbdata));
11367c478bd9Sstevel@tonic-gate 
11377c478bd9Sstevel@tonic-gate 	case TYPE_UMEM:
11387c478bd9Sstevel@tonic-gate 		if (mdb_vread(bcp, UMEM_BUFCTL_AUDIT_SIZE,
11397c478bd9Sstevel@tonic-gate 		    lkb->lkb_addr) == -1) {
11407c478bd9Sstevel@tonic-gate 			mdb_warn("unable to read bufctl at %p",
11417c478bd9Sstevel@tonic-gate 			    lkb->lkb_addr);
11427c478bd9Sstevel@tonic-gate 			return (WALK_NEXT);
11437c478bd9Sstevel@tonic-gate 		}
11447c478bd9Sstevel@tonic-gate 		return (cb(lkb->lkb_addr, bcp, cbdata));
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 	default:
11477c478bd9Sstevel@tonic-gate 		return (cb(lkb->lkb_addr, NULL, cbdata));
11487c478bd9Sstevel@tonic-gate 	}
11497c478bd9Sstevel@tonic-gate }
1150