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
leaky_verbose(char * str,uint64_t stat)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
leaky_verbose_perc(char * str,uint64_t stat,uint64_t total)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
leaky_verbose_begin(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
leaky_verbose_end(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 *
leaky_alloc(size_t sz,uint_t flags)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 *
leaky_zalloc(size_t sz,uint_t flags)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
leaky_mtabcmp(const void * l,const void * r)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
leaky_search(uintptr_t addr)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
leaky_grep(uintptr_t addr,size_t size)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
leaky_do_grep_ptr(uintptr_t loc,int process)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
leaky_grep_ptr(uintptr_t loc)438 leaky_grep_ptr(uintptr_t loc)
439 {
440 leaky_do_grep_ptr(loc, 1);
441 }
442
443 void
leaky_mark_ptr(uintptr_t loc)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
leaky_lookup_marked(uintptr_t loc,uintptr_t * addr_out,size_t * size_out)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
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)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
leaky_ctlcmp(const void * l,const void * r)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
leaky_sort(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
leaky_cleanup(int force)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
leaky_filter(const leak_pc_t * stack,int depth,uintptr_t filter)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
leaky_dump(uintptr_t filter,uint_t dump_verbose)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
findleaks_help(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
findleaks(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
leaky_walk_init(mdb_walk_state_t * wsp)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 *
leaky_walk_step_common(mdb_walk_state_t * wsp)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
leaky_walk_step(mdb_walk_state_t * wsp)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
leaky_walk_fini(mdb_walk_state_t * wsp)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
leaky_buf_walk_step(mdb_walk_state_t * wsp)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