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 #include "umem.h" 29 30 #include <sys/vmem_impl_user.h> 31 #include <umem_impl.h> 32 33 #include <alloca.h> 34 #include <libproc.h> 35 #include <stdio.h> 36 #include <string.h> 37 #include <sys/stack.h> 38 39 #include "leaky_impl.h" 40 #include "misc.h" 41 #include "proc_kludges.h" 42 43 #include "umem_pagesize.h" 44 45 /* 46 * This file defines the libumem target for ../genunix/leaky.c. 47 * 48 * See ../genunix/leaky_impl.h for the target interface definition. 49 */ 50 51 /* 52 * leaky_subr_dump_start()/_end() depend on the ordering of TYPE_VMEM, 53 * TYPE_MMAP and TYPE_SBRK. 54 */ 55 #define TYPE_MMAP 0 /* lkb_data is the size */ 56 #define TYPE_SBRK 1 /* lkb_data is the size */ 57 #define TYPE_VMEM 2 /* lkb_data is the vmem_seg's size */ 58 #define TYPE_CACHE 3 /* lkb_cid is the bufctl's cache */ 59 #define TYPE_UMEM 4 /* lkb_cid is the bufctl's cache */ 60 61 #define LKM_CTL_BUFCTL 0 /* normal allocation, PTR is bufctl */ 62 #define LKM_CTL_VMSEG 1 /* oversize allocation, PTR is vmem_seg_t */ 63 #define LKM_CTL_MEMORY 2 /* non-umem mmap or brk, PTR is region start */ 64 #define LKM_CTL_CACHE 3 /* normal alloc, non-debug, PTR is cache */ 65 #define LKM_CTL_MASK 3L 66 67 /* 68 * create a lkm_bufctl from a pointer and a type 69 */ 70 #define LKM_CTL(ptr, type) (LKM_CTLPTR(ptr) | (type)) 71 #define LKM_CTLPTR(ctl) ((uintptr_t)(ctl) & ~(LKM_CTL_MASK)) 72 #define LKM_CTLTYPE(ctl) ((uintptr_t)(ctl) & (LKM_CTL_MASK)) 73 74 static uintptr_t leak_brkbase; 75 static uintptr_t leak_brksize; 76 77 #define LEAKY_INBRK(ptr) \ 78 (((uintptr_t)(ptr) - leak_brkbase) < leak_brksize) 79 80 typedef struct leaky_seg_info { 81 uintptr_t ls_start; 82 uintptr_t ls_end; 83 } leaky_seg_info_t; 84 85 typedef struct leaky_maps { 86 leaky_seg_info_t *lm_segs; 87 uintptr_t lm_seg_count; 88 uintptr_t lm_seg_max; 89 90 pstatus_t *lm_pstatus; 91 92 leak_mtab_t **lm_lmp; 93 } leaky_maps_t; 94 95 /*ARGSUSED*/ 96 static int 97 leaky_mtab(uintptr_t addr, const umem_bufctl_audit_t *bcp, leak_mtab_t **lmp) 98 { 99 leak_mtab_t *lm = (*lmp)++; 100 101 lm->lkm_base = (uintptr_t)bcp->bc_addr; 102 lm->lkm_bufctl = LKM_CTL(addr, LKM_CTL_BUFCTL); 103 104 return (WALK_NEXT); 105 } 106 107 /*ARGSUSED*/ 108 static int 109 leaky_mtab_addr(uintptr_t addr, void *ignored, leak_mtab_t **lmp) 110 { 111 leak_mtab_t *lm = (*lmp)++; 112 113 lm->lkm_base = addr; 114 115 return (WALK_NEXT); 116 } 117 118 static int 119 leaky_seg(uintptr_t addr, const vmem_seg_t *seg, leak_mtab_t **lmp) 120 { 121 leak_mtab_t *lm = (*lmp)++; 122 123 lm->lkm_base = seg->vs_start; 124 lm->lkm_limit = seg->vs_end; 125 lm->lkm_bufctl = LKM_CTL(addr, LKM_CTL_VMSEG); 126 return (WALK_NEXT); 127 } 128 129 static int 130 leaky_vmem(uintptr_t addr, const vmem_t *vmem, leak_mtab_t **lmp) 131 { 132 if (strcmp(vmem->vm_name, "umem_oversize") != 0 && 133 strcmp(vmem->vm_name, "umem_memalign") != 0) 134 return (WALK_NEXT); 135 136 if (mdb_pwalk("vmem_alloc", (mdb_walk_cb_t)leaky_seg, lmp, addr) == -1) 137 mdb_warn("can't walk vmem_alloc for %s (%p)", vmem->vm_name, 138 addr); 139 140 return (WALK_NEXT); 141 } 142 143 /*ARGSUSED*/ 144 static int 145 leaky_estimate_vmem(uintptr_t addr, const vmem_t *vmem, size_t *est) 146 { 147 if (strcmp(vmem->vm_name, "umem_oversize") != 0 && 148 strcmp(vmem->vm_name, "umem_memalign") != 0) 149 return (WALK_NEXT); 150 151 *est += (int)(vmem->vm_kstat.vk_alloc - vmem->vm_kstat.vk_free); 152 153 return (WALK_NEXT); 154 } 155 156 static int 157 leaky_seg_cmp(const void *l, const void *r) 158 { 159 const leaky_seg_info_t *lhs = (const leaky_seg_info_t *)l; 160 const leaky_seg_info_t *rhs = (const leaky_seg_info_t *)r; 161 162 if (lhs->ls_start < rhs->ls_start) 163 return (-1); 164 if (lhs->ls_start > rhs->ls_start) 165 return (1); 166 167 return (0); 168 } 169 170 static ssize_t 171 leaky_seg_search(uintptr_t addr, leaky_seg_info_t *listp, unsigned count) 172 { 173 ssize_t left = 0, right = count - 1, guess; 174 175 while (right >= left) { 176 guess = (right + left) >> 1; 177 178 if (addr < listp[guess].ls_start) { 179 right = guess - 1; 180 continue; 181 } 182 183 if (addr >= listp[guess].ls_end) { 184 left = guess + 1; 185 continue; 186 } 187 188 return (guess); 189 } 190 191 return (-1); 192 } 193 194 /*ARGSUSED*/ 195 static int 196 leaky_count(uintptr_t addr, void *unused, size_t *total) 197 { 198 ++*total; 199 200 return (WALK_NEXT); 201 } 202 203 /*ARGSUSED*/ 204 static int 205 leaky_read_segs(uintptr_t addr, const vmem_seg_t *seg, leaky_maps_t *lmp) 206 { 207 leaky_seg_info_t *my_si = lmp->lm_segs + lmp->lm_seg_count; 208 209 if (seg->vs_start == seg->vs_end && seg->vs_start == 0) 210 return (WALK_NEXT); 211 212 if (lmp->lm_seg_count++ >= lmp->lm_seg_max) 213 return (WALK_ERR); 214 215 my_si->ls_start = seg->vs_start; 216 my_si->ls_end = seg->vs_end; 217 218 return (WALK_NEXT); 219 } 220 221 /* ARGSUSED */ 222 static int 223 leaky_process_anon_mappings(uintptr_t ignored, const prmap_t *pmp, 224 leaky_maps_t *lmp) 225 { 226 uintptr_t start = pmp->pr_vaddr; 227 uintptr_t end = pmp->pr_vaddr + pmp->pr_size; 228 229 leak_mtab_t *lm; 230 pstatus_t *Psp = lmp->lm_pstatus; 231 232 uintptr_t brk_start = Psp->pr_brkbase; 233 uintptr_t brk_end = Psp->pr_brkbase + Psp->pr_brksize; 234 235 int has_brk = 0; 236 int in_vmem = 0; 237 238 /* 239 * This checks if there is any overlap between the segment and the brk. 240 */ 241 if (end > brk_start && start < brk_end) 242 has_brk = 1; 243 244 if (leaky_seg_search(start, lmp->lm_segs, lmp->lm_seg_count) != -1) 245 in_vmem = 1; 246 247 /* 248 * We only want anonymous, mmaped memory. That means: 249 * 250 * 1. Must be read-write 251 * 2. Cannot be shared 252 * 3. Cannot have backing 253 * 4. Cannot be in the brk 254 * 5. Cannot be part of the vmem heap. 255 */ 256 if ((pmp->pr_mflags & (MA_READ | MA_WRITE)) == (MA_READ | MA_WRITE) && 257 (pmp->pr_mflags & MA_SHARED) == 0 && 258 (pmp->pr_mapname[0] == 0) && 259 !has_brk && 260 !in_vmem) { 261 dprintf(("mmaped region: [%p, %p)\n", start, end)); 262 lm = (*lmp->lm_lmp)++; 263 lm->lkm_base = start; 264 lm->lkm_limit = end; 265 lm->lkm_bufctl = LKM_CTL(pmp->pr_vaddr, LKM_CTL_MEMORY); 266 } 267 268 return (WALK_NEXT); 269 } 270 271 static void 272 leaky_handle_sbrk(leaky_maps_t *lmp) 273 { 274 uintptr_t brkbase = lmp->lm_pstatus->pr_brkbase; 275 uintptr_t brkend = brkbase + lmp->lm_pstatus->pr_brksize; 276 277 leak_mtab_t *lm; 278 279 leaky_seg_info_t *segs = lmp->lm_segs; 280 281 int x, first = -1, last = -1; 282 283 dprintf(("brk: [%p, %p)\n", brkbase, brkend)); 284 285 for (x = 0; x < lmp->lm_seg_count; x++) { 286 if (segs[x].ls_start >= brkbase && segs[x].ls_end <= brkend) { 287 if (first == -1) 288 first = x; 289 last = x; 290 } 291 } 292 293 if (brkbase == brkend) { 294 dprintf(("empty brk -- do nothing\n")); 295 } else if (first == -1) { 296 dprintf(("adding [%p, %p) whole brk\n", brkbase, brkend)); 297 298 lm = (*lmp->lm_lmp)++; 299 lm->lkm_base = brkbase; 300 lm->lkm_limit = brkend; 301 lm->lkm_bufctl = LKM_CTL(brkbase, LKM_CTL_MEMORY); 302 } else { 303 uintptr_t curbrk = P2ROUNDUP(brkbase, umem_pagesize); 304 305 if (curbrk != segs[first].ls_start) { 306 dprintf(("adding [%p, %p) in brk, before first seg\n", 307 brkbase, segs[first].ls_start)); 308 309 lm = (*lmp->lm_lmp)++; 310 lm->lkm_base = brkbase; 311 lm->lkm_limit = segs[first].ls_start; 312 lm->lkm_bufctl = LKM_CTL(brkbase, LKM_CTL_MEMORY); 313 314 curbrk = segs[first].ls_start; 315 316 } else if (curbrk != brkbase) { 317 dprintf(("ignore [%p, %p) -- realign\n", brkbase, 318 curbrk)); 319 } 320 321 for (x = first; x <= last; x++) { 322 if (curbrk < segs[x].ls_start) { 323 dprintf(("adding [%p, %p) in brk\n", curbrk, 324 segs[x].ls_start)); 325 326 lm = (*lmp->lm_lmp)++; 327 lm->lkm_base = curbrk; 328 lm->lkm_limit = segs[x].ls_start; 329 lm->lkm_bufctl = LKM_CTL(curbrk, 330 LKM_CTL_MEMORY); 331 } 332 curbrk = segs[x].ls_end; 333 } 334 335 if (curbrk < brkend) { 336 dprintf(("adding [%p, %p) in brk, after last seg\n", 337 curbrk, brkend)); 338 339 lm = (*lmp->lm_lmp)++; 340 lm->lkm_base = curbrk; 341 lm->lkm_limit = brkend; 342 lm->lkm_bufctl = LKM_CTL(curbrk, LKM_CTL_MEMORY); 343 } 344 } 345 } 346 347 static int 348 leaky_handle_anon_mappings(leak_mtab_t **lmp) 349 { 350 leaky_maps_t lm; 351 352 vmem_t *heap_arena; 353 vmem_t *vm_next; 354 vmem_t *heap_top; 355 vmem_t vmem; 356 357 pstatus_t Ps; 358 359 if (mdb_get_xdata("pstatus", &Ps, sizeof (Ps)) == -1) { 360 mdb_warn("couldn't read pstatus xdata"); 361 return (DCMD_ERR); 362 } 363 lm.lm_pstatus = &Ps; 364 365 leak_brkbase = Ps.pr_brkbase; 366 leak_brksize = Ps.pr_brksize; 367 368 if (umem_readvar(&heap_arena, "heap_arena") == -1) { 369 mdb_warn("couldn't read heap_arena"); 370 return (DCMD_ERR); 371 } 372 373 if (heap_arena == NULL) { 374 mdb_warn("heap_arena is NULL.\n"); 375 return (DCMD_ERR); 376 } 377 378 for (vm_next = heap_arena; vm_next != NULL; vm_next = vmem.vm_source) { 379 if (mdb_vread(&vmem, sizeof (vmem), (uintptr_t)vm_next) == -1) { 380 mdb_warn("couldn't read vmem at %p", vm_next); 381 return (DCMD_ERR); 382 } 383 heap_top = vm_next; 384 } 385 386 lm.lm_seg_count = 0; 387 lm.lm_seg_max = 0; 388 389 if (mdb_pwalk("vmem_span", (mdb_walk_cb_t)leaky_count, 390 &lm.lm_seg_max, (uintptr_t)heap_top) == -1) { 391 mdb_warn("couldn't walk vmem_span for vmem %p", heap_top); 392 return (DCMD_ERR); 393 } 394 lm.lm_segs = mdb_alloc(lm.lm_seg_max * sizeof (*lm.lm_segs), 395 UM_SLEEP | UM_GC); 396 397 if (mdb_pwalk("vmem_span", (mdb_walk_cb_t)leaky_read_segs, &lm, 398 (uintptr_t)heap_top) == -1) { 399 mdb_warn("couldn't walk vmem_span for vmem %p", 400 heap_top); 401 return (DCMD_ERR); 402 } 403 404 if (lm.lm_seg_count > lm.lm_seg_max) { 405 mdb_warn("segment list for vmem %p grew\n", heap_top); 406 return (DCMD_ERR); 407 } 408 409 qsort(lm.lm_segs, lm.lm_seg_count, sizeof (*lm.lm_segs), leaky_seg_cmp); 410 411 lm.lm_lmp = lmp; 412 413 prockludge_add_walkers(); 414 415 if (mdb_walk(KLUDGE_MAPWALK_NAME, 416 (mdb_walk_cb_t)leaky_process_anon_mappings, &lm) == -1) { 417 mdb_warn("Couldn't walk "KLUDGE_MAPWALK_NAME); 418 prockludge_remove_walkers(); 419 return (DCMD_ERR); 420 } 421 422 prockludge_remove_walkers(); 423 leaky_handle_sbrk(&lm); 424 425 return (DCMD_OK); 426 } 427 428 static int 429 leaky_interested(const umem_cache_t *c) 430 { 431 vmem_t vmem; 432 433 if (mdb_vread(&vmem, sizeof (vmem), (uintptr_t)c->cache_arena) == -1) { 434 mdb_warn("cannot read arena %p for cache '%s'", 435 (uintptr_t)c->cache_arena, c->cache_name); 436 return (0); 437 } 438 439 /* 440 * If this cache isn't allocating from either the umem_default or 441 * umem_firewall vmem arena, we're not interested. 442 */ 443 if (strcmp(vmem.vm_name, "umem_default") != 0 && 444 strcmp(vmem.vm_name, "umem_firewall") != 0) { 445 dprintf(("Skipping cache '%s' with arena '%s'\n", 446 c->cache_name, vmem.vm_name)); 447 return (0); 448 } 449 450 return (1); 451 } 452 453 /*ARGSUSED*/ 454 static int 455 leaky_estimate(uintptr_t addr, const umem_cache_t *c, size_t *est) 456 { 457 if (!leaky_interested(c)) 458 return (WALK_NEXT); 459 460 *est += umem_estimate_allocated(addr, c); 461 462 return (WALK_NEXT); 463 } 464 465 /*ARGSUSED*/ 466 static int 467 leaky_cache(uintptr_t addr, const umem_cache_t *c, leak_mtab_t **lmp) 468 { 469 leak_mtab_t *lm = *lmp; 470 mdb_walk_cb_t cb; 471 const char *walk; 472 int audit = (c->cache_flags & UMF_AUDIT); 473 474 if (!leaky_interested(c)) 475 return (WALK_NEXT); 476 477 if (audit) { 478 walk = "bufctl"; 479 cb = (mdb_walk_cb_t)leaky_mtab; 480 } else { 481 walk = "umem"; 482 cb = (mdb_walk_cb_t)leaky_mtab_addr; 483 } 484 if (mdb_pwalk(walk, cb, lmp, addr) == -1) { 485 mdb_warn("can't walk umem for cache %p (%s)", addr, 486 c->cache_name); 487 return (WALK_DONE); 488 } 489 490 for (; lm < *lmp; lm++) { 491 lm->lkm_limit = lm->lkm_base + c->cache_bufsize; 492 if (!audit) 493 lm->lkm_bufctl = LKM_CTL(addr, LKM_CTL_CACHE); 494 } 495 return (WALK_NEXT); 496 } 497 498 static char *map_head = "%-?s %?s %-10s used reason\n"; 499 static char *map_fmt = "[%?p,%?p) %-10s "; 500 #define BACKING_LEN 10 /* must match the third field's width in map_fmt */ 501 502 static void 503 leaky_mappings_header(void) 504 { 505 dprintf((map_head, "mapping", "", "backing")); 506 } 507 508 /* ARGSUSED */ 509 static int 510 leaky_grep_mappings(uintptr_t ignored, const prmap_t *pmp, 511 const pstatus_t *Psp) 512 { 513 const char *map_libname_ptr; 514 char db_mp_name[BACKING_LEN+1]; 515 516 map_libname_ptr = strrchr(pmp->pr_mapname, '/'); 517 if (map_libname_ptr != NULL) 518 map_libname_ptr++; 519 else 520 map_libname_ptr = pmp->pr_mapname; 521 522 strlcpy(db_mp_name, map_libname_ptr, sizeof (db_mp_name)); 523 524 dprintf((map_fmt, pmp->pr_vaddr, (char *)pmp->pr_vaddr + pmp->pr_size, 525 db_mp_name)); 526 527 #define USE(rsn) dprintf_cont(("yes %s\n", (rsn))) 528 #define IGNORE(rsn) dprintf_cont(("no %s\n", (rsn))) 529 530 if (!(pmp->pr_mflags & MA_WRITE) || !(pmp->pr_mflags & MA_READ)) { 531 IGNORE("read-only"); 532 } else if (pmp->pr_vaddr <= Psp->pr_brkbase && 533 pmp->pr_vaddr + pmp->pr_size > Psp->pr_brkbase) { 534 USE("bss"); /* grab up to brkbase */ 535 leaky_grep(pmp->pr_vaddr, Psp->pr_brkbase - pmp->pr_vaddr); 536 } else if (pmp->pr_vaddr >= Psp->pr_brkbase && 537 pmp->pr_vaddr < Psp->pr_brkbase + Psp->pr_brksize) { 538 IGNORE("in brk"); 539 } else if (pmp->pr_vaddr == Psp->pr_stkbase && 540 pmp->pr_size == Psp->pr_stksize) { 541 IGNORE("stack"); 542 } else if (0 == strcmp(map_libname_ptr, "a.out")) { 543 USE("a.out data"); 544 leaky_grep(pmp->pr_vaddr, pmp->pr_size); 545 } else if (0 == strncmp(map_libname_ptr, "libumem.so", 10)) { 546 IGNORE("part of umem"); 547 } else if (pmp->pr_mapname[0] != 0) { 548 USE("lib data"); /* library data/bss */ 549 leaky_grep(pmp->pr_vaddr, pmp->pr_size); 550 } else if ((pmp->pr_mflags & MA_ANON) && pmp->pr_mapname[0] == 0) { 551 IGNORE("anon"); 552 } else { 553 IGNORE(""); /* default to ignoring */ 554 } 555 556 #undef USE 557 #undef IGNORE 558 559 return (WALK_NEXT); 560 } 561 562 /*ARGSUSED*/ 563 static int 564 leaky_mark_lwp(void *ignored, const lwpstatus_t *lwp) 565 { 566 leaky_mark_ptr(lwp->pr_reg[R_SP] + STACK_BIAS); 567 return (0); 568 } 569 570 /*ARGSUSED*/ 571 static int 572 leaky_process_lwp(void *ignored, const lwpstatus_t *lwp) 573 { 574 const uintptr_t *regs = (const uintptr_t *)&lwp->pr_reg; 575 int i; 576 uintptr_t sp; 577 uintptr_t addr; 578 size_t size; 579 580 for (i = 0; i < R_SP; i++) 581 leaky_grep_ptr(regs[i]); 582 583 sp = regs[i++] + STACK_BIAS; 584 if (leaky_lookup_marked(sp, &addr, &size)) 585 leaky_grep(sp, size - (sp - addr)); 586 587 for (; i < NPRGREG; i++) 588 leaky_grep_ptr(regs[i]); 589 590 return (0); 591 } 592 593 /* 594 * Handles processing various proc-related things: 595 * 1. calls leaky_process_lwp on each the LWP 596 * 2. leaky_greps the bss/data of libraries and a.out, and the a.out stack. 597 */ 598 static int 599 leaky_process_proc(void) 600 { 601 pstatus_t Ps; 602 struct ps_prochandle *Pr; 603 604 if (mdb_get_xdata("pstatus", &Ps, sizeof (Ps)) == -1) { 605 mdb_warn("couldn't read pstatus xdata"); 606 return (DCMD_ERR); 607 } 608 609 dprintf(("pstatus says:\n")); 610 dprintf(("\tbrk: base %p size %p\n", 611 Ps.pr_brkbase, Ps.pr_brksize)); 612 dprintf(("\tstk: base %p size %p\n", 613 Ps.pr_stkbase, Ps.pr_stksize)); 614 615 if (mdb_get_xdata("pshandle", &Pr, sizeof (Pr)) == -1) { 616 mdb_warn("couldn't read pshandle xdata"); 617 return (DCMD_ERR); 618 } 619 620 if (Plwp_iter(Pr, leaky_mark_lwp, NULL) != 0) { 621 mdb_warn("findleaks: Failed to iterate lwps\n"); 622 return (DCMD_ERR); 623 } 624 625 if (Plwp_iter(Pr, leaky_process_lwp, NULL) != 0) { 626 mdb_warn("findleaks: Failed to iterate lwps\n"); 627 return (DCMD_ERR); 628 } 629 630 prockludge_add_walkers(); 631 632 leaky_mappings_header(); 633 634 if (mdb_walk(KLUDGE_MAPWALK_NAME, (mdb_walk_cb_t)leaky_grep_mappings, 635 &Ps) == -1) { 636 mdb_warn("Couldn't walk "KLUDGE_MAPWALK_NAME); 637 prockludge_remove_walkers(); 638 return (-1); 639 } 640 641 prockludge_remove_walkers(); 642 643 return (0); 644 } 645 646 static void 647 leaky_subr_caller(const uintptr_t *stack, uint_t depth, char *buf, 648 uintptr_t *pcp) 649 { 650 int i; 651 GElf_Sym sym; 652 uintptr_t pc = 0; 653 654 buf[0] = 0; 655 656 for (i = 0; i < depth; i++) { 657 pc = stack[i]; 658 659 if (mdb_lookup_by_addr(pc, 660 MDB_SYM_FUZZY, buf, MDB_SYM_NAMLEN, &sym) == -1) 661 continue; 662 if (strncmp(buf, "libumem.so", 10) == 0) 663 continue; 664 665 *pcp = pc; 666 return; 667 } 668 669 /* 670 * We're only here if the entire call chain is in libumem.so; 671 * this shouldn't happen, but we'll just use the last caller. 672 */ 673 *pcp = pc; 674 } 675 676 int 677 leaky_subr_bufctl_cmp(const leak_bufctl_t *lhs, const leak_bufctl_t *rhs) 678 { 679 char lbuf[MDB_SYM_NAMLEN], rbuf[MDB_SYM_NAMLEN]; 680 uintptr_t lcaller, rcaller; 681 int rval; 682 683 leaky_subr_caller(lhs->lkb_stack, lhs->lkb_depth, lbuf, &lcaller); 684 leaky_subr_caller(rhs->lkb_stack, lhs->lkb_depth, rbuf, &rcaller); 685 686 if (rval = strcmp(lbuf, rbuf)) 687 return (rval); 688 689 if (lcaller < rcaller) 690 return (-1); 691 692 if (lcaller > rcaller) 693 return (1); 694 695 if (lhs->lkb_data < rhs->lkb_data) 696 return (-1); 697 698 if (lhs->lkb_data > rhs->lkb_data) 699 return (1); 700 701 return (0); 702 } 703 704 /*ARGSUSED*/ 705 int 706 leaky_subr_estimate(size_t *estp) 707 { 708 if (umem_ready == 0) { 709 mdb_warn( 710 "findleaks: umem is not loaded in the address space\n"); 711 return (DCMD_ERR); 712 } 713 714 if (umem_ready == UMEM_READY_INIT_FAILED) { 715 mdb_warn("findleaks: umem initialization failed -- no " 716 "possible leaks.\n"); 717 return (DCMD_ERR); 718 } 719 720 if (umem_ready != UMEM_READY) { 721 mdb_warn("findleaks: No allocations have occured -- no " 722 "possible leaks.\n"); 723 return (DCMD_ERR); 724 } 725 726 if (mdb_walk("umem_cache", (mdb_walk_cb_t)leaky_estimate, estp) == -1) { 727 mdb_warn("couldn't walk 'umem_cache'"); 728 return (DCMD_ERR); 729 } 730 731 if (mdb_walk("vmem", (mdb_walk_cb_t)leaky_estimate_vmem, estp) == -1) { 732 mdb_warn("couldn't walk 'vmem'"); 733 return (DCMD_ERR); 734 } 735 736 if (*estp == 0) { 737 mdb_warn("findleaks: No allocated buffers found.\n"); 738 return (DCMD_ERR); 739 } 740 741 prockludge_add_walkers(); 742 743 if (mdb_walk(KLUDGE_MAPWALK_NAME, (mdb_walk_cb_t)leaky_count, 744 estp) == -1) { 745 mdb_warn("Couldn't walk "KLUDGE_MAPWALK_NAME); 746 prockludge_remove_walkers(); 747 return (DCMD_ERR); 748 } 749 750 prockludge_remove_walkers(); 751 752 return (DCMD_OK); 753 } 754 755 int 756 leaky_subr_fill(leak_mtab_t **lmpp) 757 { 758 if (leaky_handle_anon_mappings(lmpp) != DCMD_OK) { 759 mdb_warn("unable to process mappings\n"); 760 return (DCMD_ERR); 761 } 762 763 if (mdb_walk("vmem", (mdb_walk_cb_t)leaky_vmem, lmpp) == -1) { 764 mdb_warn("couldn't walk 'vmem'"); 765 return (DCMD_ERR); 766 } 767 768 if (mdb_walk("umem_cache", (mdb_walk_cb_t)leaky_cache, lmpp) == -1) { 769 mdb_warn("couldn't walk 'umem_cache'"); 770 return (DCMD_ERR); 771 } 772 773 return (DCMD_OK); 774 } 775 776 int 777 leaky_subr_run(void) 778 { 779 if (leaky_process_proc() == DCMD_ERR) { 780 mdb_warn("failed to process proc"); 781 return (DCMD_ERR); 782 } 783 return (DCMD_OK); 784 } 785 786 void 787 leaky_subr_add_leak(leak_mtab_t *lmp) 788 { 789 uintptr_t addr = LKM_CTLPTR(lmp->lkm_bufctl); 790 uint_t depth; 791 792 vmem_seg_t vs; 793 umem_bufctl_audit_t *bcp; 794 UMEM_LOCAL_BUFCTL_AUDIT(&bcp); 795 796 switch (LKM_CTLTYPE(lmp->lkm_bufctl)) { 797 case LKM_CTL_BUFCTL: 798 if (mdb_vread(bcp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) { 799 mdb_warn("couldn't read leaked bufctl at addr %p", 800 addr); 801 return; 802 } 803 804 depth = MIN(bcp->bc_depth, umem_stack_depth); 805 806 /* 807 * The top of the stack will be in umem_cache_alloc(). 808 * Since the offset in umem_cache_alloc() isn't interesting 809 * we skip that frame for the purposes of uniquifying stacks. 810 * 811 * Also, we use the cache pointer as the leaks's cid, to 812 * prevent the coalescing of leaks from different caches. 813 */ 814 if (depth > 0) 815 depth--; 816 leaky_add_leak(TYPE_UMEM, addr, (uintptr_t)bcp->bc_addr, 817 bcp->bc_timestamp, bcp->bc_stack + 1, depth, 818 (uintptr_t)bcp->bc_cache, (uintptr_t)bcp->bc_cache); 819 break; 820 case LKM_CTL_VMSEG: 821 if (mdb_vread(&vs, sizeof (vs), addr) == -1) { 822 mdb_warn("couldn't read leaked vmem_seg at addr %p", 823 addr); 824 return; 825 } 826 depth = MIN(vs.vs_depth, VMEM_STACK_DEPTH); 827 828 leaky_add_leak(TYPE_VMEM, addr, vs.vs_start, vs.vs_timestamp, 829 vs.vs_stack, depth, 0, (vs.vs_end - vs.vs_start)); 830 break; 831 case LKM_CTL_MEMORY: 832 if (LEAKY_INBRK(addr)) 833 leaky_add_leak(TYPE_SBRK, addr, addr, 0, NULL, 0, 0, 834 lmp->lkm_limit - addr); 835 else 836 leaky_add_leak(TYPE_MMAP, addr, addr, 0, NULL, 0, 0, 837 lmp->lkm_limit - addr); 838 break; 839 case LKM_CTL_CACHE: 840 leaky_add_leak(TYPE_CACHE, lmp->lkm_base, lmp->lkm_base, 0, 841 NULL, 0, addr, addr); 842 break; 843 default: 844 mdb_warn("internal error: invalid leak_bufctl_t\n"); 845 break; 846 } 847 } 848 849 static int lk_vmem_seen; 850 static int lk_cache_seen; 851 static int lk_umem_seen; 852 static size_t lk_ttl; 853 static size_t lk_bytes; 854 855 void 856 leaky_subr_dump_start(int type) 857 { 858 switch (type) { 859 case TYPE_MMAP: 860 lk_vmem_seen = 0; 861 break; 862 863 case TYPE_SBRK: 864 case TYPE_VMEM: 865 return; /* don't zero counts */ 866 867 case TYPE_CACHE: 868 lk_cache_seen = 0; 869 break; 870 871 case TYPE_UMEM: 872 lk_umem_seen = 0; 873 break; 874 875 default: 876 break; 877 } 878 879 lk_ttl = 0; 880 lk_bytes = 0; 881 } 882 883 void 884 leaky_subr_dump(const leak_bufctl_t *lkb, int verbose) 885 { 886 const leak_bufctl_t *cur; 887 umem_cache_t cache; 888 size_t min, max, size; 889 char sz[30]; 890 char c[MDB_SYM_NAMLEN]; 891 uintptr_t caller; 892 const char *nm, *nm_lc; 893 uint8_t type = lkb->lkb_type; 894 895 if (verbose) { 896 lk_ttl = 0; 897 lk_bytes = 0; 898 } else if (!lk_vmem_seen && (type == TYPE_VMEM || type == TYPE_MMAP || 899 type == TYPE_SBRK)) { 900 lk_vmem_seen = 1; 901 mdb_printf("%-16s %7s %?s %s\n", 902 "BYTES", "LEAKED", "VMEM_SEG", "CALLER"); 903 } 904 905 switch (lkb->lkb_type) { 906 case TYPE_MMAP: 907 case TYPE_SBRK: 908 nm = (lkb->lkb_type == TYPE_MMAP) ? "MMAP" : "SBRK"; 909 nm_lc = (lkb->lkb_type == TYPE_MMAP) ? "mmap(2)" : "sbrk(2)"; 910 911 for (; lkb != NULL; lkb = lkb->lkb_next) { 912 if (!verbose) 913 mdb_printf("%-16d %7d %?p %s\n", lkb->lkb_data, 914 lkb->lkb_dups + 1, lkb->lkb_addr, nm); 915 else 916 mdb_printf("%s leak: [%p, %p), %ld bytes\n", 917 nm_lc, lkb->lkb_addr, 918 lkb->lkb_addr + lkb->lkb_data, 919 lkb->lkb_data); 920 lk_ttl++; 921 lk_bytes += lkb->lkb_data; 922 } 923 return; 924 925 case TYPE_VMEM: 926 min = max = lkb->lkb_data; 927 928 for (cur = lkb; cur != NULL; cur = cur->lkb_next) { 929 size = cur->lkb_data; 930 931 if (size < min) 932 min = size; 933 if (size > max) 934 max = size; 935 936 lk_ttl++; 937 lk_bytes += size; 938 } 939 940 if (min == max) 941 (void) mdb_snprintf(sz, sizeof (sz), "%ld", min); 942 else 943 (void) mdb_snprintf(sz, sizeof (sz), "%ld-%ld", 944 min, max); 945 946 if (!verbose) { 947 leaky_subr_caller(lkb->lkb_stack, lkb->lkb_depth, 948 c, &caller); 949 950 mdb_printf("%-16s %7d %?p %a\n", sz, lkb->lkb_dups + 1, 951 lkb->lkb_addr, caller); 952 } else { 953 mdb_arg_t v; 954 955 if (lk_ttl == 1) 956 mdb_printf("umem_oversize leak: 1 vmem_seg, " 957 "%ld bytes\n", lk_bytes); 958 else 959 mdb_printf("umem_oversize leak: %d vmem_segs, " 960 "%s bytes each, %ld bytes total\n", 961 lk_ttl, sz, lk_bytes); 962 963 v.a_type = MDB_TYPE_STRING; 964 v.a_un.a_str = "-v"; 965 966 if (mdb_call_dcmd("vmem_seg", lkb->lkb_addr, 967 DCMD_ADDRSPEC, 1, &v) == -1) { 968 mdb_warn("'%p::vmem_seg -v' failed", 969 lkb->lkb_addr); 970 } 971 } 972 return; 973 974 case TYPE_CACHE: 975 if (!lk_cache_seen) { 976 lk_cache_seen = 1; 977 if (lk_vmem_seen) 978 mdb_printf("\n"); 979 mdb_printf("%-?s %7s %?s %s\n", 980 "CACHE", "LEAKED", "BUFFER", "CALLER"); 981 } 982 983 if (mdb_vread(&cache, sizeof (cache), lkb->lkb_data) == -1) { 984 /* 985 * This _really_ shouldn't happen; we shouldn't 986 * have been able to get this far if this 987 * cache wasn't readable. 988 */ 989 mdb_warn("can't read cache %p for leaked " 990 "buffer %p", lkb->lkb_data, lkb->lkb_addr); 991 return; 992 } 993 994 lk_ttl += lkb->lkb_dups + 1; 995 lk_bytes += (lkb->lkb_dups + 1) * cache.cache_bufsize; 996 997 caller = (lkb->lkb_depth == 0) ? 0 : lkb->lkb_stack[0]; 998 if (caller != 0) { 999 (void) mdb_snprintf(c, sizeof (c), "%a", caller); 1000 } else { 1001 (void) mdb_snprintf(c, sizeof (c), "%s", 1002 (verbose) ? "" : "?"); 1003 } 1004 1005 if (!verbose) { 1006 mdb_printf("%0?p %7d %0?p %s\n", lkb->lkb_cid, 1007 lkb->lkb_dups + 1, lkb->lkb_addr, c); 1008 } else { 1009 if (lk_ttl == 1) 1010 mdb_printf("%s leak: 1 buffer, %ld bytes,\n", 1011 cache.cache_name, lk_bytes); 1012 else 1013 mdb_printf("%s leak: %d buffers, " 1014 "%ld bytes each, %ld bytes total,\n", 1015 cache.cache_name, lk_ttl, 1016 cache.cache_bufsize, lk_bytes); 1017 mdb_printf(" %s%s%ssample addr %p\n", 1018 (caller == 0) ? "" : "caller ", c, 1019 (caller == 0) ? "" : ", ", lkb->lkb_addr); 1020 } 1021 return; 1022 1023 case TYPE_UMEM: 1024 if (!lk_umem_seen) { 1025 lk_umem_seen = 1; 1026 if (lk_vmem_seen || lk_cache_seen) 1027 mdb_printf("\n"); 1028 mdb_printf("%-?s %7s %?s %s\n", 1029 "CACHE", "LEAKED", "BUFCTL", "CALLER"); 1030 } 1031 if (mdb_vread(&cache, sizeof (cache), lkb->lkb_data) == -1) { 1032 /* 1033 * This _really_ shouldn't happen; we shouldn't 1034 * have been able to get this far if this 1035 * cache wasn't readable. 1036 */ 1037 mdb_warn("can't read cache %p for leaked " 1038 "bufctl %p", lkb->lkb_data, lkb->lkb_addr); 1039 return; 1040 } 1041 1042 lk_ttl += lkb->lkb_dups + 1; 1043 lk_bytes += (lkb->lkb_dups + 1) * cache.cache_bufsize; 1044 1045 if (!verbose) { 1046 leaky_subr_caller(lkb->lkb_stack, lkb->lkb_depth, c, 1047 &caller); 1048 1049 mdb_printf("%0?p %7d %0?p %a\n", lkb->lkb_data, 1050 lkb->lkb_dups + 1, lkb->lkb_addr, caller); 1051 } else { 1052 mdb_arg_t v; 1053 1054 if (lk_ttl == 1) 1055 mdb_printf("%s leak: 1 buffer, %ld bytes\n", 1056 cache.cache_name, lk_bytes); 1057 else 1058 mdb_printf("%s leak: %d buffers, " 1059 "%ld bytes each, %ld bytes total\n", 1060 cache.cache_name, lk_ttl, 1061 cache.cache_bufsize, lk_bytes); 1062 1063 v.a_type = MDB_TYPE_STRING; 1064 v.a_un.a_str = "-v"; 1065 1066 if (mdb_call_dcmd("bufctl", lkb->lkb_addr, 1067 DCMD_ADDRSPEC, 1, &v) == -1) { 1068 mdb_warn("'%p::bufctl -v' failed", 1069 lkb->lkb_addr); 1070 } 1071 } 1072 return; 1073 1074 default: 1075 return; 1076 } 1077 } 1078 1079 void 1080 leaky_subr_dump_end(int type) 1081 { 1082 int i; 1083 int width; 1084 const char *leak; 1085 1086 switch (type) { 1087 case TYPE_VMEM: 1088 if (!lk_vmem_seen) 1089 return; 1090 1091 width = 16; 1092 leak = "oversized leak"; 1093 break; 1094 1095 case TYPE_CACHE: 1096 if (!lk_cache_seen) 1097 return; 1098 1099 width = sizeof (uintptr_t) * 2; 1100 leak = "buffer"; 1101 break; 1102 1103 case TYPE_UMEM: 1104 if (!lk_umem_seen) 1105 return; 1106 1107 width = sizeof (uintptr_t) * 2; 1108 leak = "buffer"; 1109 break; 1110 1111 default: 1112 return; 1113 } 1114 1115 for (i = 0; i < 72; i++) 1116 mdb_printf("-"); 1117 mdb_printf("\n%*s %7ld %s%s, %ld byte%s\n", 1118 width, "Total", lk_ttl, leak, (lk_ttl == 1) ? "" : "s", 1119 lk_bytes, (lk_bytes == 1) ? "" : "s"); 1120 } 1121 1122 int 1123 leaky_subr_invoke_callback(const leak_bufctl_t *lkb, mdb_walk_cb_t cb, 1124 void *cbdata) 1125 { 1126 vmem_seg_t vs; 1127 umem_bufctl_audit_t *bcp; 1128 UMEM_LOCAL_BUFCTL_AUDIT(&bcp); 1129 1130 switch (lkb->lkb_type) { 1131 case TYPE_VMEM: 1132 if (mdb_vread(&vs, sizeof (vs), lkb->lkb_addr) == -1) { 1133 mdb_warn("unable to read vmem_seg at %p", 1134 lkb->lkb_addr); 1135 return (WALK_NEXT); 1136 } 1137 return (cb(lkb->lkb_addr, &vs, cbdata)); 1138 1139 case TYPE_UMEM: 1140 if (mdb_vread(bcp, UMEM_BUFCTL_AUDIT_SIZE, 1141 lkb->lkb_addr) == -1) { 1142 mdb_warn("unable to read bufctl at %p", 1143 lkb->lkb_addr); 1144 return (WALK_NEXT); 1145 } 1146 return (cb(lkb->lkb_addr, bcp, cbdata)); 1147 1148 default: 1149 return (cb(lkb->lkb_addr, NULL, cbdata)); 1150 } 1151 } 1152