xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/leaky.c (revision 1a220b56b93ff1dc80855691548503117af4cc10)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * A generic memory leak detector.  The target interface, defined in
30  * <leaky_impl.h>, is implemented by the genunix and libumem dmods to fill
31  * in the details of operation.
32  */
33 
34 #include <mdb/mdb_modapi.h>
35 
36 #include "leaky.h"
37 #include "leaky_impl.h"
38 
39 #define	LK_BUFCTLHSIZE	127
40 
41 /*
42  * We re-use the low bit of the lkm_addr as the 'marked' bit.
43  */
44 #define	LK_MARKED(b)	((uintptr_t)(b) & 1)
45 #define	LK_MARK(b)	((b) |= 1)
46 #define	LK_ADDR(b)	((uintptr_t)(b) & ~1UL)
47 
48 /*
49  * Possible values for lk_state.
50  */
51 #define	LK_CLEAN	0	/* No outstanding mdb_alloc()'s */
52 #define	LK_SWEEPING	1	/* Potentially some outstanding mdb_alloc()'s */
53 #define	LK_DONE		2	/* All mdb_alloc()'s complete */
54 #define	LK_CLEANING	3	/* Currently cleaning prior mdb_alloc()'s */
55 
56 static volatile int lk_state;
57 
58 #define	LK_STATE_SIZE	10000	/* completely arbitrary */
59 
60 typedef int leak_ndx_t;		/* change if >2 billion buffers are needed */
61 
62 typedef struct leak_state {
63 	struct leak_state *lks_next;
64 	leak_ndx_t lks_stack[LK_STATE_SIZE];
65 } leak_state_t;
66 
67 typedef struct leak_beans {
68 	int lkb_dups;
69 	int lkb_follows;
70 	int lkb_misses;
71 	int lkb_dismissals;
72 	int lkb_pushes;
73 	int lkb_deepest;
74 } leak_beans_t;
75 
76 typedef struct leak_type {
77 	int		lt_type;
78 	size_t		lt_leaks;
79 	leak_bufctl_t	**lt_sorted;
80 } leak_type_t;
81 
82 typedef struct leak_walk {
83 	int lkw_ndx;
84 	leak_bufctl_t *lkw_current;
85 	leak_bufctl_t *lkw_hash_next;
86 } leak_walk_t;
87 
88 #define	LK_SCAN_BUFFER_SIZE	16384
89 static uintptr_t *lk_scan_buffer;
90 
91 static leak_mtab_t *lk_mtab;
92 static leak_state_t *lk_free_state;
93 static leak_ndx_t lk_nbuffers;
94 static leak_beans_t lk_beans;
95 static leak_bufctl_t *lk_bufctl[LK_BUFCTLHSIZE];
96 static leak_type_t lk_types[LK_NUM_TYPES];
97 static size_t lk_memusage;
98 #ifndef _KMDB
99 static hrtime_t lk_begin;
100 static hrtime_t lk_vbegin;
101 #endif
102 static uint_t lk_verbose = FALSE;
103 
104 static void
105 leaky_verbose(char *str, uint64_t stat)
106 {
107 	if (lk_verbose == FALSE)
108 		return;
109 
110 	mdb_printf("findleaks: ");
111 
112 	if (str == NULL) {
113 		mdb_printf("\n");
114 		return;
115 	}
116 
117 	mdb_printf("%*s => %lld\n", 30, str, stat);
118 }
119 
120 static void
121 leaky_verbose_perc(char *str, uint64_t stat, uint64_t total)
122 {
123 	uint_t perc = (stat * 100) / total;
124 	uint_t tenths = ((stat * 1000) / total) % 10;
125 
126 	if (lk_verbose == FALSE)
127 		return;
128 
129 	mdb_printf("findleaks: %*s => %-13lld (%2d.%1d%%)\n",
130 	    30, str, stat, perc, tenths);
131 }
132 
133 static void
134 leaky_verbose_begin(void)
135 {
136 	/* kmdb can't tell time */
137 #ifndef _KMDB
138 	extern hrtime_t gethrvtime(void);
139 	lk_begin = gethrtime();
140 	lk_vbegin = gethrvtime();
141 #endif
142 	lk_memusage = 0;
143 }
144 
145 static void
146 leaky_verbose_end(void)
147 {
148 	/* kmdb can't tell time */
149 #ifndef _KMDB
150 	extern hrtime_t gethrvtime(void);
151 
152 	hrtime_t ts = gethrtime() - lk_begin;
153 	hrtime_t sec = ts / (hrtime_t)NANOSEC;
154 	hrtime_t nsec = ts % (hrtime_t)NANOSEC;
155 
156 	hrtime_t vts = gethrvtime() - lk_vbegin;
157 	hrtime_t vsec = vts / (hrtime_t)NANOSEC;
158 	hrtime_t vnsec = vts % (hrtime_t)NANOSEC;
159 #endif
160 
161 	if (lk_verbose == FALSE)
162 		return;
163 
164 	mdb_printf("findleaks: %*s => %lu kB\n",
165 	    30, "peak memory usage", (lk_memusage + 1023)/1024);
166 #ifndef _KMDB
167 	mdb_printf("findleaks: %*s => %lld.%lld seconds\n",
168 	    30, "elapsed CPU time", vsec, (vnsec * 10)/(hrtime_t)NANOSEC);
169 	mdb_printf("findleaks: %*s => %lld.%lld seconds\n",
170 	    30, "elapsed wall time", sec, (nsec * 10)/(hrtime_t)NANOSEC);
171 #endif
172 	leaky_verbose(NULL, 0);
173 }
174 
175 static void *
176 leaky_alloc(size_t sz, uint_t flags)
177 {
178 	void *buf = mdb_alloc(sz, flags);
179 
180 	if (buf != NULL)
181 		lk_memusage += sz;
182 
183 	return (buf);
184 }
185 
186 static void *
187 leaky_zalloc(size_t sz, uint_t flags)
188 {
189 	void *buf = mdb_zalloc(sz, flags);
190 
191 	if (buf != NULL)
192 		lk_memusage += sz;
193 
194 	return (buf);
195 }
196 
197 static int
198 leaky_mtabcmp(const void *l, const void *r)
199 {
200 	const leak_mtab_t *lhs = (const leak_mtab_t *)l;
201 	const leak_mtab_t *rhs = (const leak_mtab_t *)r;
202 
203 	if (lhs->lkm_base < rhs->lkm_base)
204 		return (-1);
205 	if (lhs->lkm_base > rhs->lkm_base)
206 		return (1);
207 
208 	return (0);
209 }
210 
211 static leak_ndx_t
212 leaky_search(uintptr_t addr)
213 {
214 	leak_ndx_t left = 0, right = lk_nbuffers - 1, guess;
215 
216 	while (right >= left) {
217 		guess = (right + left) >> 1;
218 
219 		if (addr < LK_ADDR(lk_mtab[guess].lkm_base)) {
220 			right = guess - 1;
221 			continue;
222 		}
223 
224 		if (addr >= lk_mtab[guess].lkm_limit) {
225 			left = guess + 1;
226 			continue;
227 		}
228 
229 		return (guess);
230 	}
231 
232 	return (-1);
233 }
234 
235 void
236 leaky_grep(uintptr_t addr, size_t size)
237 {
238 	uintptr_t *buf, *cur, *end;
239 	size_t bytes, newsz, nptrs;
240 	leak_state_t *state = NULL, *new_state;
241 	uint_t state_idx;
242 	uintptr_t min = LK_ADDR(lk_mtab[0].lkm_base);
243 	uintptr_t max = lk_mtab[lk_nbuffers - 1].lkm_limit;
244 	int dups = 0, misses = 0, depth = 0, deepest = 0;
245 	int follows = 0, dismissals = 0, pushes = 0;
246 	leak_ndx_t mtab_ndx;
247 	leak_mtab_t *lmp;
248 	uintptr_t nbase;
249 	uintptr_t base;
250 	size_t base_size;
251 	const uintptr_t mask = sizeof (uintptr_t) - 1;
252 
253 	if (addr == NULL || size == 0)
254 		return;
255 
256 	state_idx = 0;
257 
258 	/*
259 	 * Our main loop, led by the 'pop' label:
260 	 *	1)  read in a buffer piece by piece,
261 	 *	2)  mark all unmarked mtab entries reachable from it, and
262 	 *	    either scan them in-line or push them onto our stack of
263 	 *	    unfinished work.
264 	 *	3)  pop the top mtab entry off the stack, and loop.
265 	 */
266 pop:
267 	base = addr;
268 	base_size = size;
269 
270 	/*
271 	 * If our address isn't pointer-aligned, we need to align it and
272 	 * whack the size appropriately.
273 	 */
274 	if (size < mask) {
275 		size = 0;
276 	} else if (addr & mask) {
277 		size -= (mask + 1) - (addr & mask);
278 		addr += (mask + 1) - (addr & mask);
279 	}
280 	size -= (size & mask);
281 
282 	while (size > 0) {
283 		buf = lk_scan_buffer;
284 		end = &buf[LK_SCAN_BUFFER_SIZE / sizeof (uintptr_t)];
285 
286 		bytes = MIN(size, LK_SCAN_BUFFER_SIZE);
287 		cur = end - (bytes / sizeof (uintptr_t));
288 
289 		if (mdb_vread(cur, bytes, addr) == -1) {
290 			mdb_warn("[%p, %p): couldn't read %ld bytes at %p",
291 			    base, base + base_size, bytes, addr);
292 			break;
293 		}
294 
295 		addr += bytes;
296 		size -= bytes;
297 
298 		/*
299 		 * The buffer looks like:  ('+'s are unscanned data)
300 		 *
301 		 * -----------------------------++++++++++++++++
302 		 * |				|		|
303 		 * buf				cur		end
304 		 *
305 		 * cur scans forward.  When we encounter a new buffer, and
306 		 * it will fit behind "cur", we read it in and back up cur,
307 		 * processing it immediately.
308 		 */
309 		while (cur < end) {
310 			uintptr_t ptr = *cur++;
311 
312 			if (ptr < min || ptr > max) {
313 				dismissals++;
314 				continue;
315 			}
316 
317 			if ((mtab_ndx = leaky_search(ptr)) == -1) {
318 				misses++;
319 				continue;
320 			}
321 
322 			lmp = &lk_mtab[mtab_ndx];
323 			if (LK_MARKED(lmp->lkm_base)) {
324 				dups++;			/* already seen */
325 				continue;
326 			}
327 
328 			/*
329 			 * Found an unmarked buffer.  Mark it, then either
330 			 * read it in, or add it to the stack of pending work.
331 			 */
332 			follows++;
333 			LK_MARK(lmp->lkm_base);
334 
335 			nbase = LK_ADDR(lmp->lkm_base);
336 			newsz = lmp->lkm_limit - nbase;
337 
338 			nptrs = newsz / sizeof (uintptr_t);
339 			newsz = nptrs * sizeof (uintptr_t);
340 
341 			if ((nbase & mask) == 0 && nptrs <= (cur - buf) &&
342 			    mdb_vread(cur - nptrs, newsz, nbase) != -1) {
343 				cur -= nptrs;
344 				continue;
345 			}
346 
347 			/*
348 			 * couldn't process it in-place -- add it to the
349 			 * stack.
350 			 */
351 			if (state == NULL || state_idx == LK_STATE_SIZE) {
352 				if ((new_state = lk_free_state) != NULL)
353 					lk_free_state = new_state->lks_next;
354 				else
355 					new_state = leaky_zalloc(
356 					    sizeof (*state), UM_SLEEP | UM_GC);
357 
358 				new_state->lks_next = state;
359 				state = new_state;
360 				state_idx = 0;
361 			}
362 
363 			pushes++;
364 			state->lks_stack[state_idx++] = mtab_ndx;
365 			if (++depth > deepest)
366 				deepest = depth;
367 		}
368 	}
369 
370 	/*
371 	 * Retrieve the next mtab index, extract its info, and loop around
372 	 * to process it.
373 	 */
374 	if (state_idx == 0 && state != NULL) {
375 		new_state = state->lks_next;
376 
377 		state->lks_next = lk_free_state;
378 		lk_free_state = state;
379 
380 		state = new_state;
381 		state_idx = LK_STATE_SIZE;
382 	}
383 
384 	if (depth > 0) {
385 		mtab_ndx = state->lks_stack[--state_idx];
386 
387 		addr = LK_ADDR(lk_mtab[mtab_ndx].lkm_base);
388 		size = lk_mtab[mtab_ndx].lkm_limit - addr;
389 		depth--;
390 
391 		goto pop;
392 	}
393 
394 	/*
395 	 * update the beans
396 	 */
397 	lk_beans.lkb_dups += dups;
398 	lk_beans.lkb_dismissals += dismissals;
399 	lk_beans.lkb_misses += misses;
400 	lk_beans.lkb_follows += follows;
401 	lk_beans.lkb_pushes += pushes;
402 
403 	if (deepest > lk_beans.lkb_deepest)
404 		lk_beans.lkb_deepest = deepest;
405 }
406 
407 static void
408 leaky_do_grep_ptr(uintptr_t loc, int process)
409 {
410 	leak_ndx_t ndx;
411 	leak_mtab_t *lkmp;
412 	size_t sz;
413 
414 	if (loc < LK_ADDR(lk_mtab[0].lkm_base) ||
415 	    loc > lk_mtab[lk_nbuffers - 1].lkm_limit) {
416 		lk_beans.lkb_dismissals++;
417 		return;
418 	}
419 	if ((ndx = leaky_search(loc)) == -1) {
420 		lk_beans.lkb_misses++;
421 		return;
422 	}
423 
424 	lkmp = &lk_mtab[ndx];
425 	sz = lkmp->lkm_limit - lkmp->lkm_base;
426 
427 	if (LK_MARKED(lkmp->lkm_base)) {
428 		lk_beans.lkb_dups++;
429 	} else {
430 		LK_MARK(lkmp->lkm_base);
431 		lk_beans.lkb_follows++;
432 		if (process)
433 			leaky_grep(lkmp->lkm_base, sz);
434 	}
435 }
436 
437 void
438 leaky_grep_ptr(uintptr_t loc)
439 {
440 	leaky_do_grep_ptr(loc, 1);
441 }
442 
443 void
444 leaky_mark_ptr(uintptr_t loc)
445 {
446 	leaky_do_grep_ptr(loc, 0);
447 }
448 
449 /*
450  * This may be used to manually process a marked buffer.
451  */
452 int
453 leaky_lookup_marked(uintptr_t loc, uintptr_t *addr_out, size_t *size_out)
454 {
455 	leak_ndx_t ndx;
456 	leak_mtab_t *lkmp;
457 
458 	if ((ndx = leaky_search(loc)) == -1)
459 		return (0);
460 
461 	lkmp = &lk_mtab[ndx];
462 	*addr_out = LK_ADDR(lkmp->lkm_base);
463 	*size_out = lkmp->lkm_limit - LK_ADDR(lkmp->lkm_base);
464 	return (1);
465 }
466 
467 void
468 leaky_add_leak(int type, uintptr_t addr, uintptr_t bufaddr, hrtime_t timestamp,
469     leak_pc_t *stack, uint_t depth, uintptr_t cid, uintptr_t data)
470 {
471 	leak_bufctl_t *nlkb, *lkb;
472 	uintptr_t total = 0;
473 	size_t ndx;
474 	int i;
475 
476 	if (type < 0 || type >= LK_NUM_TYPES || depth != (uint8_t)depth) {
477 		mdb_warn("invalid arguments to leaky_add_leak()\n");
478 		return;
479 	}
480 
481 	nlkb = leaky_zalloc(LEAK_BUFCTL_SIZE(depth), UM_SLEEP);
482 	nlkb->lkb_type = type;
483 	nlkb->lkb_addr = addr;
484 	nlkb->lkb_bufaddr = bufaddr;
485 	nlkb->lkb_cid = cid;
486 	nlkb->lkb_data = data;
487 	nlkb->lkb_depth = depth;
488 	nlkb->lkb_timestamp = timestamp;
489 
490 	total = type;
491 	for (i = 0; i < depth; i++) {
492 		total += stack[i];
493 		nlkb->lkb_stack[i] = stack[i];
494 	}
495 
496 	ndx = total % LK_BUFCTLHSIZE;
497 
498 	if ((lkb = lk_bufctl[ndx]) == NULL) {
499 		lk_types[type].lt_leaks++;
500 		lk_bufctl[ndx] = nlkb;
501 		return;
502 	}
503 
504 	for (;;) {
505 		if (lkb->lkb_type != type || lkb->lkb_depth != depth ||
506 		    lkb->lkb_cid != cid)
507 			goto no_match;
508 
509 		for (i = 0; i < depth; i++)
510 			if (lkb->lkb_stack[i] != stack[i])
511 				goto no_match;
512 
513 		/*
514 		 * If we're here, we've found a matching stack; link it in.
515 		 * Note that the volatile cast assures that these stores
516 		 * will occur in program order (thus assuring that we can
517 		 * take an interrupt and still be in a sane enough state to
518 		 * throw away the data structure later, in leaky_cleanup()).
519 		 */
520 		((volatile leak_bufctl_t *)nlkb)->lkb_next = lkb->lkb_next;
521 		((volatile leak_bufctl_t *)lkb)->lkb_next = nlkb;
522 		lkb->lkb_dups++;
523 
524 		/*
525 		 * If we're older, swap places so that we are the
526 		 * representative leak.
527 		 */
528 		if (timestamp < lkb->lkb_timestamp) {
529 			nlkb->lkb_addr = lkb->lkb_addr;
530 			nlkb->lkb_bufaddr = lkb->lkb_bufaddr;
531 			nlkb->lkb_data = lkb->lkb_data;
532 			nlkb->lkb_timestamp = lkb->lkb_timestamp;
533 
534 			lkb->lkb_addr = addr;
535 			lkb->lkb_bufaddr = bufaddr;
536 			lkb->lkb_data = data;
537 			lkb->lkb_timestamp = timestamp;
538 		}
539 		break;
540 
541 no_match:
542 		if (lkb->lkb_hash_next == NULL) {
543 			lkb->lkb_hash_next = nlkb;
544 			lk_types[type].lt_leaks++;
545 			break;
546 		}
547 		lkb = lkb->lkb_hash_next;
548 	}
549 }
550 
551 int
552 leaky_ctlcmp(const void *l, const void *r)
553 {
554 	const leak_bufctl_t *lhs = *((const leak_bufctl_t **)l);
555 	const leak_bufctl_t *rhs = *((const leak_bufctl_t **)r);
556 
557 	return (leaky_subr_bufctl_cmp(lhs, rhs));
558 }
559 
560 void
561 leaky_sort(void)
562 {
563 	int type, i, j;
564 	leak_bufctl_t *lkb;
565 	leak_type_t *ltp;
566 
567 	for (type = 0; type < LK_NUM_TYPES; type++) {
568 		ltp = &lk_types[type];
569 
570 		if (ltp->lt_leaks == 0)
571 			continue;
572 
573 		ltp->lt_sorted = leaky_alloc(ltp->lt_leaks *
574 		    sizeof (leak_bufctl_t *), UM_SLEEP);
575 
576 		j = 0;
577 		for (i = 0; i < LK_BUFCTLHSIZE; i++) {
578 			for (lkb = lk_bufctl[i]; lkb != NULL;
579 			    lkb = lkb->lkb_hash_next) {
580 				if (lkb->lkb_type == type)
581 					ltp->lt_sorted[j++] = lkb;
582 			}
583 		}
584 		if (j != ltp->lt_leaks)
585 			mdb_warn("expected %d leaks, got %d\n", ltp->lt_leaks,
586 			    j);
587 
588 		qsort(ltp->lt_sorted, ltp->lt_leaks, sizeof (leak_bufctl_t *),
589 		    leaky_ctlcmp);
590 	}
591 }
592 
593 void
594 leaky_cleanup(int force)
595 {
596 	int i;
597 	leak_bufctl_t *lkb, *l, *next;
598 
599 	/*
600 	 * State structures are allocated UM_GC, so we just need to nuke
601 	 * the freelist pointer.
602 	 */
603 	lk_free_state = NULL;
604 
605 	switch (lk_state) {
606 	case LK_CLEAN:
607 		return;		/* nothing to do */
608 
609 	case LK_CLEANING:
610 		mdb_warn("interrupted during ::findleaks cleanup; some mdb "
611 		    "memory will be leaked\n");
612 
613 		for (i = 0; i < LK_BUFCTLHSIZE; i++)
614 			lk_bufctl[i] = NULL;
615 
616 		for (i = 0; i < LK_NUM_TYPES; i++) {
617 			lk_types[i].lt_leaks = 0;
618 			lk_types[i].lt_sorted = NULL;
619 		}
620 
621 		bzero(&lk_beans, sizeof (lk_beans));
622 		lk_state = LK_CLEAN;
623 		return;
624 
625 	case LK_SWEEPING:
626 		break;		/* must clean up */
627 
628 	case LK_DONE:
629 	default:
630 		if (!force)
631 			return;
632 		break;		/* only clean up if forced */
633 	}
634 
635 	lk_state = LK_CLEANING;
636 
637 	for (i = 0; i < LK_NUM_TYPES; i++) {
638 		if (lk_types[i].lt_sorted != NULL) {
639 			mdb_free(lk_types[i].lt_sorted,
640 			    lk_types[i].lt_leaks * sizeof (leak_bufctl_t *));
641 			lk_types[i].lt_sorted = NULL;
642 		}
643 		lk_types[i].lt_leaks = 0;
644 	}
645 
646 	for (i = 0; i < LK_BUFCTLHSIZE; i++) {
647 		for (lkb = lk_bufctl[i]; lkb != NULL; lkb = next) {
648 			for (l = lkb->lkb_next; l != NULL; l = next) {
649 				next = l->lkb_next;
650 				mdb_free(l, LEAK_BUFCTL_SIZE(l->lkb_depth));
651 			}
652 			next = lkb->lkb_hash_next;
653 			mdb_free(lkb, LEAK_BUFCTL_SIZE(lkb->lkb_depth));
654 		}
655 		lk_bufctl[i] = NULL;
656 	}
657 
658 	bzero(&lk_beans, sizeof (lk_beans));
659 	lk_state = LK_CLEAN;
660 }
661 
662 int
663 leaky_filter(const leak_pc_t *stack, int depth, uintptr_t filter)
664 {
665 	int i;
666 	GElf_Sym sym;
667 	char c;
668 
669 	if (filter == NULL)
670 		return (1);
671 
672 	for (i = 0; i < depth; i++) {
673 		if (stack[i] == filter)
674 			return (1);
675 
676 		if (mdb_lookup_by_addr(stack[i], MDB_SYM_FUZZY,
677 		    &c, sizeof (c), &sym) == -1)
678 			continue;
679 
680 		if ((uintptr_t)sym.st_value == filter)
681 			return (1);
682 	}
683 
684 	return (0);
685 }
686 
687 void
688 leaky_dump(uintptr_t filter, uint_t dump_verbose)
689 {
690 	int i;
691 	size_t leaks;
692 	leak_bufctl_t **sorted;
693 	leak_bufctl_t *lkb;
694 	int seen = 0;
695 
696 	for (i = 0; i < LK_NUM_TYPES; i++) {
697 		leaks = lk_types[i].lt_leaks;
698 		sorted = lk_types[i].lt_sorted;
699 
700 		leaky_subr_dump_start(i);
701 		while (leaks-- > 0) {
702 			lkb = *sorted++;
703 
704 			if (!leaky_filter(lkb->lkb_stack, lkb->lkb_depth,
705 			    filter))
706 				continue;
707 
708 			seen = 1;
709 			leaky_subr_dump(lkb, 0);
710 		}
711 		leaky_subr_dump_end(i);
712 	}
713 
714 	if (!seen) {
715 		if (filter != NULL)
716 			mdb_printf(
717 			    "findleaks: no memory leaks matching %a found\n",
718 			    filter);
719 		else
720 			mdb_printf(
721 			    "findleaks: no memory leaks detected\n");
722 	}
723 
724 	if (!dump_verbose || !seen)
725 		return;
726 
727 	mdb_printf("\n");
728 
729 	for (i = 0; i < LK_NUM_TYPES; i++) {
730 		leaks = lk_types[i].lt_leaks;
731 		sorted = lk_types[i].lt_sorted;
732 
733 		while (leaks-- > 0) {
734 			lkb = *sorted++;
735 
736 			if (!leaky_filter(lkb->lkb_stack, lkb->lkb_depth,
737 			    filter))
738 				continue;
739 
740 			leaky_subr_dump(lkb, 1);
741 		}
742 	}
743 }
744 
745 static const char *const findleaks_desc =
746 	"Does a conservative garbage collection of the heap in order to find\n"
747 	"potentially leaked buffers.  Similar leaks are coalesced by stack\n"
748 	"trace, with the oldest leak picked as representative.  The leak\n"
749 	"table is cached between invocations.\n"
750 	"\n"
751 	"addr, if provided, should be a function or PC location.  Reported\n"
752 	"leaks will then be limited to those with that function or PC in\n"
753 	"their stack trace.\n"
754 	"\n"
755 	"The 'leak' and 'leakbuf' walkers can be used to retrieve coalesced\n"
756 	"leaks.\n";
757 
758 static const char *const findleaks_args =
759 	"  -d    detail each representative leak (long)\n"
760 	"  -f    throw away cached state, and do a full run\n"
761 	"  -v    report verbose information about the findleaks run\n";
762 
763 void
764 findleaks_help(void)
765 {
766 	mdb_printf("%s\n", findleaks_desc);
767 	mdb_dec_indent(2);
768 	mdb_printf("%<b>OPTIONS%</b>\n");
769 	mdb_inc_indent(2);
770 	mdb_printf("%s", findleaks_args);
771 }
772 
773 #define	LK_REPORT_BEAN(x) leaky_verbose_perc(#x, lk_beans.lkb_##x, total);
774 
775 /*ARGSUSED*/
776 int
777 findleaks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
778 {
779 	size_t est = 0;
780 	leak_ndx_t i;
781 	leak_mtab_t *lmp;
782 	ssize_t total;
783 	uintptr_t filter = NULL;
784 	uint_t dump = 0;
785 	uint_t force = 0;
786 	uint_t verbose = 0;
787 	int ret;
788 
789 	if (flags & DCMD_ADDRSPEC)
790 		filter = addr;
791 
792 	if (mdb_getopts(argc, argv,
793 	    'd', MDB_OPT_SETBITS, TRUE, &dump,
794 	    'f', MDB_OPT_SETBITS, TRUE, &force,
795 	    'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL) != argc)
796 		return (DCMD_USAGE);
797 
798 	if (verbose || force)
799 		lk_verbose = verbose;
800 
801 	/*
802 	 * Clean any previous ::findleaks.
803 	 */
804 	leaky_cleanup(force);
805 
806 	if (lk_state == LK_DONE) {
807 		if (lk_verbose)
808 			mdb_printf("findleaks: using cached results "
809 			    "(use '-f' to force a full run)\n");
810 		goto dump;
811 	}
812 
813 	leaky_verbose_begin();
814 
815 	if ((ret = leaky_subr_estimate(&est)) != DCMD_OK)
816 		return (ret);
817 
818 	leaky_verbose("maximum buffers", est);
819 
820 	/*
821 	 * Now we have an upper bound on the number of buffers.  Allocate
822 	 * our mtab array.
823 	 */
824 	lk_mtab = leaky_zalloc(est * sizeof (leak_mtab_t), UM_SLEEP | UM_GC);
825 	lmp = lk_mtab;
826 
827 	if ((ret = leaky_subr_fill(&lmp)) != DCMD_OK)
828 		return (ret);
829 
830 	lk_nbuffers = lmp - lk_mtab;
831 
832 	qsort(lk_mtab, lk_nbuffers, sizeof (leak_mtab_t), leaky_mtabcmp);
833 
834 	/*
835 	 * validate the mtab table now that it is sorted
836 	 */
837 	for (i = 0; i < lk_nbuffers; i++) {
838 		if (lk_mtab[i].lkm_base >= lk_mtab[i].lkm_limit) {
839 			mdb_warn("[%p, %p): invalid mtab\n",
840 			    lk_mtab[i].lkm_base, lk_mtab[i].lkm_limit);
841 			return (DCMD_ERR);
842 		}
843 
844 		if (i < lk_nbuffers - 1 &&
845 		    lk_mtab[i].lkm_limit > lk_mtab[i + 1].lkm_base) {
846 			mdb_warn("[%p, %p) and [%p, %p): overlapping mtabs\n",
847 			    lk_mtab[i].lkm_base, lk_mtab[i].lkm_limit,
848 			    lk_mtab[i + 1].lkm_base, lk_mtab[i + 1].lkm_limit);
849 			return (DCMD_ERR);
850 		}
851 	}
852 
853 	leaky_verbose("actual buffers", lk_nbuffers);
854 
855 	lk_scan_buffer = leaky_zalloc(LK_SCAN_BUFFER_SIZE, UM_SLEEP | UM_GC);
856 
857 	if ((ret = leaky_subr_run()) != DCMD_OK)
858 		return (ret);
859 
860 	lk_state = LK_SWEEPING;
861 
862 	for (i = 0; i < lk_nbuffers; i++) {
863 		if (LK_MARKED(lk_mtab[i].lkm_base))
864 			continue;
865 		leaky_subr_add_leak(&lk_mtab[i]);
866 	}
867 
868 	total = lk_beans.lkb_dismissals + lk_beans.lkb_misses +
869 	    lk_beans.lkb_dups + lk_beans.lkb_follows;
870 
871 	leaky_verbose(NULL, 0);
872 	leaky_verbose("potential pointers", total);
873 	LK_REPORT_BEAN(dismissals);
874 	LK_REPORT_BEAN(misses);
875 	LK_REPORT_BEAN(dups);
876 	LK_REPORT_BEAN(follows);
877 
878 	leaky_verbose(NULL, 0);
879 	leaky_verbose_end();
880 
881 	leaky_sort();
882 	lk_state = LK_DONE;
883 dump:
884 	leaky_dump(filter, dump);
885 
886 	return (DCMD_OK);
887 }
888 
889 int
890 leaky_walk_init(mdb_walk_state_t *wsp)
891 {
892 	leak_walk_t *lw;
893 	leak_bufctl_t *lkb, *cur;
894 
895 	uintptr_t addr;
896 	int i;
897 
898 	if (lk_state != LK_DONE) {
899 		mdb_warn("::findleaks must be run %sbefore leaks can be"
900 		    " walked\n", lk_state != LK_CLEAN ? "to completion " : "");
901 		return (WALK_ERR);
902 	}
903 
904 	if (wsp->walk_addr == NULL) {
905 		lkb = NULL;
906 		goto found;
907 	}
908 
909 	addr = wsp->walk_addr;
910 
911 	/*
912 	 * Search the representative leaks first, since that's what we
913 	 * report in the table.  If that fails, search everything.
914 	 *
915 	 * Note that we goto found with lkb as the head of desired dup list.
916 	 */
917 	for (i = 0; i < LK_BUFCTLHSIZE; i++) {
918 		for (lkb = lk_bufctl[i]; lkb != NULL; lkb = lkb->lkb_hash_next)
919 			if (lkb->lkb_addr == addr)
920 				goto found;
921 	}
922 
923 	for (i = 0; i < LK_BUFCTLHSIZE; i++) {
924 		for (lkb = lk_bufctl[i]; lkb != NULL; lkb = lkb->lkb_hash_next)
925 			for (cur = lkb; cur != NULL; cur = cur->lkb_next)
926 				if (cur->lkb_addr == addr)
927 					goto found;
928 	}
929 
930 	mdb_warn("%p is not a leaked ctl address\n", addr);
931 	return (WALK_ERR);
932 
933 found:
934 	wsp->walk_data = lw = mdb_zalloc(sizeof (*lw), UM_SLEEP);
935 	lw->lkw_ndx = 0;
936 	lw->lkw_current = lkb;
937 	lw->lkw_hash_next = NULL;
938 
939 	return (WALK_NEXT);
940 }
941 
942 leak_bufctl_t *
943 leaky_walk_step_common(mdb_walk_state_t *wsp)
944 {
945 	leak_walk_t *lw = wsp->walk_data;
946 	leak_bufctl_t *lk;
947 
948 	if ((lk = lw->lkw_current) == NULL) {
949 		if ((lk = lw->lkw_hash_next) == NULL) {
950 			if (wsp->walk_addr)
951 				return (NULL);
952 
953 			while (lk == NULL && lw->lkw_ndx < LK_BUFCTLHSIZE)
954 				lk = lk_bufctl[lw->lkw_ndx++];
955 
956 			if (lw->lkw_ndx == LK_BUFCTLHSIZE)
957 				return (NULL);
958 		}
959 
960 		lw->lkw_hash_next = lk->lkb_hash_next;
961 	}
962 
963 	lw->lkw_current = lk->lkb_next;
964 	return (lk);
965 }
966 
967 int
968 leaky_walk_step(mdb_walk_state_t *wsp)
969 {
970 	leak_bufctl_t *lk;
971 
972 	if ((lk = leaky_walk_step_common(wsp)) == NULL)
973 		return (WALK_DONE);
974 
975 	return (leaky_subr_invoke_callback(lk, wsp->walk_callback,
976 	    wsp->walk_cbdata));
977 }
978 
979 void
980 leaky_walk_fini(mdb_walk_state_t *wsp)
981 {
982 	leak_walk_t *lw = wsp->walk_data;
983 
984 	mdb_free(lw, sizeof (leak_walk_t));
985 }
986 
987 int
988 leaky_buf_walk_step(mdb_walk_state_t *wsp)
989 {
990 	leak_bufctl_t *lk;
991 
992 	if ((lk = leaky_walk_step_common(wsp)) == NULL)
993 		return (WALK_DONE);
994 
995 	return (wsp->walk_callback(lk->lkb_bufaddr, NULL, wsp->walk_cbdata));
996 }
997