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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include "umem.h" 27 #include <libproc.h> 28 #include <mdb/mdb_modapi.h> 29 30 #include "kgrep.h" 31 #include "leaky.h" 32 #include "misc.h" 33 #include "proc_kludges.h" 34 35 #include <umem_impl.h> 36 #include <sys/vmem_impl_user.h> 37 38 #include "umem_pagesize.h" 39 40 typedef struct datafmt { 41 char *hdr1; 42 char *hdr2; 43 char *dashes; 44 char *fmt; 45 } datafmt_t; 46 47 static datafmt_t umemfmt[] = { 48 { "cache ", "name ", 49 "-------------------------", "%-25s " }, 50 { " buf", " size", "------", "%6u " }, 51 { " buf", "in use", "------", "%6u " }, 52 { " buf", " total", "------", "%6u " }, 53 { " memory", " in use", "---------", "%9u " }, 54 { " alloc", " succeed", "---------", "%9u " }, 55 { "alloc", " fail", "-----", "%5llu " }, 56 { NULL, NULL, NULL, NULL } 57 }; 58 59 static datafmt_t vmemfmt[] = { 60 { "vmem ", "name ", 61 "-------------------------", "%-*s " }, 62 { " memory", " in use", "---------", "%9llu " }, 63 { " memory", " total", "----------", "%10llu " }, 64 { " memory", " import", "---------", "%9llu " }, 65 { " alloc", " succeed", "---------", "%9llu " }, 66 { "alloc", " fail", "-----", "%5llu " }, 67 { NULL, NULL, NULL, NULL } 68 }; 69 70 /*ARGSUSED*/ 71 static int 72 umastat_cpu_avail(uintptr_t addr, const umem_cpu_cache_t *ccp, int *avail) 73 { 74 if (ccp->cc_rounds > 0) 75 *avail += ccp->cc_rounds; 76 if (ccp->cc_prounds > 0) 77 *avail += ccp->cc_prounds; 78 79 return (WALK_NEXT); 80 } 81 82 /*ARGSUSED*/ 83 static int 84 umastat_cpu_alloc(uintptr_t addr, const umem_cpu_cache_t *ccp, int *alloc) 85 { 86 *alloc += ccp->cc_alloc; 87 88 return (WALK_NEXT); 89 } 90 91 /*ARGSUSED*/ 92 static int 93 umastat_slab_avail(uintptr_t addr, const umem_slab_t *sp, int *avail) 94 { 95 *avail += sp->slab_chunks - sp->slab_refcnt; 96 97 return (WALK_NEXT); 98 } 99 100 typedef struct umastat_vmem { 101 uintptr_t kv_addr; 102 struct umastat_vmem *kv_next; 103 int kv_meminuse; 104 int kv_alloc; 105 int kv_fail; 106 } umastat_vmem_t; 107 108 static int 109 umastat_cache(uintptr_t addr, const umem_cache_t *cp, umastat_vmem_t **kvp) 110 { 111 umastat_vmem_t *kv; 112 datafmt_t *dfp = umemfmt; 113 int magsize; 114 115 int avail, alloc, total; 116 size_t meminuse = (cp->cache_slab_create - cp->cache_slab_destroy) * 117 cp->cache_slabsize; 118 119 mdb_walk_cb_t cpu_avail = (mdb_walk_cb_t)umastat_cpu_avail; 120 mdb_walk_cb_t cpu_alloc = (mdb_walk_cb_t)umastat_cpu_alloc; 121 mdb_walk_cb_t slab_avail = (mdb_walk_cb_t)umastat_slab_avail; 122 123 magsize = umem_get_magsize(cp); 124 125 alloc = cp->cache_slab_alloc + cp->cache_full.ml_alloc; 126 avail = cp->cache_full.ml_total * magsize; 127 total = cp->cache_buftotal; 128 129 (void) mdb_pwalk("umem_cpu_cache", cpu_alloc, &alloc, addr); 130 (void) mdb_pwalk("umem_cpu_cache", cpu_avail, &avail, addr); 131 (void) mdb_pwalk("umem_slab_partial", slab_avail, &avail, addr); 132 133 for (kv = *kvp; kv != NULL; kv = kv->kv_next) { 134 if (kv->kv_addr == (uintptr_t)cp->cache_arena) 135 goto out; 136 } 137 138 kv = mdb_zalloc(sizeof (umastat_vmem_t), UM_SLEEP | UM_GC); 139 kv->kv_next = *kvp; 140 kv->kv_addr = (uintptr_t)cp->cache_arena; 141 *kvp = kv; 142 out: 143 kv->kv_meminuse += meminuse; 144 kv->kv_alloc += alloc; 145 kv->kv_fail += cp->cache_alloc_fail; 146 147 mdb_printf((dfp++)->fmt, cp->cache_name); 148 mdb_printf((dfp++)->fmt, cp->cache_bufsize); 149 mdb_printf((dfp++)->fmt, total - avail); 150 mdb_printf((dfp++)->fmt, total); 151 mdb_printf((dfp++)->fmt, meminuse); 152 mdb_printf((dfp++)->fmt, alloc); 153 mdb_printf((dfp++)->fmt, cp->cache_alloc_fail); 154 mdb_printf("\n"); 155 156 return (WALK_NEXT); 157 } 158 159 static int 160 umastat_vmem_totals(uintptr_t addr, const vmem_t *v, umastat_vmem_t *kv) 161 { 162 while (kv != NULL && kv->kv_addr != addr) 163 kv = kv->kv_next; 164 165 if (kv == NULL || kv->kv_alloc == 0) 166 return (WALK_NEXT); 167 168 mdb_printf("Total [%s]%*s %6s %6s %6s %9u %9u %5u\n", v->vm_name, 169 17 - strlen(v->vm_name), "", "", "", "", 170 kv->kv_meminuse, kv->kv_alloc, kv->kv_fail); 171 172 return (WALK_NEXT); 173 } 174 175 /*ARGSUSED*/ 176 static int 177 umastat_vmem(uintptr_t addr, const vmem_t *v, void *ignored) 178 { 179 datafmt_t *dfp = vmemfmt; 180 uintptr_t paddr; 181 vmem_t parent; 182 int ident = 0; 183 184 for (paddr = (uintptr_t)v->vm_source; paddr != NULL; ident += 4) { 185 if (mdb_vread(&parent, sizeof (parent), paddr) == -1) { 186 mdb_warn("couldn't trace %p's ancestry", addr); 187 ident = 0; 188 break; 189 } 190 paddr = (uintptr_t)parent.vm_source; 191 } 192 193 mdb_printf("%*s", ident, ""); 194 mdb_printf((dfp++)->fmt, 25 - ident, v->vm_name); 195 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_inuse); 196 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_total); 197 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_import); 198 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_alloc); 199 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_fail); 200 201 mdb_printf("\n"); 202 203 return (WALK_NEXT); 204 } 205 206 /*ARGSUSED*/ 207 int 208 umastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 209 { 210 umastat_vmem_t *kv = NULL; 211 datafmt_t *dfp; 212 213 if (argc != 0) 214 return (DCMD_USAGE); 215 216 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++) 217 mdb_printf("%s ", dfp->hdr1); 218 mdb_printf("\n"); 219 220 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++) 221 mdb_printf("%s ", dfp->hdr2); 222 mdb_printf("\n"); 223 224 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++) 225 mdb_printf("%s ", dfp->dashes); 226 mdb_printf("\n"); 227 228 if (mdb_walk("umem_cache", (mdb_walk_cb_t)umastat_cache, &kv) == -1) { 229 mdb_warn("can't walk 'umem_cache'"); 230 return (DCMD_ERR); 231 } 232 233 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++) 234 mdb_printf("%s ", dfp->dashes); 235 mdb_printf("\n"); 236 237 if (mdb_walk("vmem", (mdb_walk_cb_t)umastat_vmem_totals, kv) == -1) { 238 mdb_warn("can't walk 'vmem'"); 239 return (DCMD_ERR); 240 } 241 242 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++) 243 mdb_printf("%s ", dfp->dashes); 244 mdb_printf("\n"); 245 246 mdb_printf("\n"); 247 248 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++) 249 mdb_printf("%s ", dfp->hdr1); 250 mdb_printf("\n"); 251 252 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++) 253 mdb_printf("%s ", dfp->hdr2); 254 mdb_printf("\n"); 255 256 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++) 257 mdb_printf("%s ", dfp->dashes); 258 mdb_printf("\n"); 259 260 if (mdb_walk("vmem", (mdb_walk_cb_t)umastat_vmem, NULL) == -1) { 261 mdb_warn("can't walk 'vmem'"); 262 return (DCMD_ERR); 263 } 264 265 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++) 266 mdb_printf("%s ", dfp->dashes); 267 mdb_printf("\n"); 268 return (DCMD_OK); 269 } 270 271 /* 272 * kmdb doesn't use libproc, and thus doesn't have any prmap_t's to walk. 273 * We have other ways to grep kmdb's address range. 274 */ 275 #ifndef _KMDB 276 277 typedef struct ugrep_walk_data { 278 kgrep_cb_func *ug_cb; 279 void *ug_cbdata; 280 } ugrep_walk_data_t; 281 282 /*ARGSUSED*/ 283 int 284 ugrep_mapping_cb(uintptr_t addr, const void *prm_arg, void *data) 285 { 286 ugrep_walk_data_t *ug = data; 287 const prmap_t *prm = prm_arg; 288 289 return (ug->ug_cb(prm->pr_vaddr, prm->pr_vaddr + prm->pr_size, 290 ug->ug_cbdata)); 291 } 292 293 int 294 kgrep_subr(kgrep_cb_func *cb, void *cbdata) 295 { 296 ugrep_walk_data_t ug; 297 298 prockludge_add_walkers(); 299 300 ug.ug_cb = cb; 301 ug.ug_cbdata = cbdata; 302 303 if (mdb_walk(KLUDGE_MAPWALK_NAME, ugrep_mapping_cb, &ug) == -1) { 304 mdb_warn("Unable to walk "KLUDGE_MAPWALK_NAME); 305 return (DCMD_ERR); 306 } 307 308 prockludge_remove_walkers(); 309 return (DCMD_OK); 310 } 311 312 size_t 313 kgrep_subr_pagesize(void) 314 { 315 return (PAGESIZE); 316 } 317 318 #endif /* !_KMDB */ 319 320 static const mdb_dcmd_t dcmds[] = { 321 322 /* from libumem.c */ 323 { "umastat", NULL, "umem allocator stats", umastat }, 324 325 /* from misc.c */ 326 { "umem_debug", NULL, "toggle umem dcmd/walk debugging", umem_debug}, 327 328 /* from umem.c */ 329 { "umem_status", NULL, "Print umem status and message buffer", 330 umem_status }, 331 { "allocdby", ":", "given a thread, print its allocated buffers", 332 allocdby }, 333 { "bufctl", ":[-vh] [-a addr] [-c caller] [-e earliest] [-l latest] " 334 "[-t thd]", "print or filter a bufctl", bufctl, bufctl_help }, 335 { "bufctl_audit", ":", "print a bufctl_audit", bufctl_audit }, 336 { "freedby", ":", "given a thread, print its freed buffers", freedby }, 337 { "umalog", "[ fail | slab ]", 338 "display umem transaction log and stack traces", umalog }, 339 { "umausers", "[-ef] [cache ...]", "display current medium and large " 340 "users of the umem allocator", umausers }, 341 { "umem_cache", "?", "print a umem cache", umem_cache }, 342 { "umem_log", "?", "dump umem transaction log", umem_log }, 343 { "umem_malloc_dist", "[-dg] [-b maxbins] [-B minbinsize]", 344 "report distribution of outstanding malloc()s", 345 umem_malloc_dist, umem_malloc_dist_help }, 346 { "umem_malloc_info", "?[-dg] [-b maxbins] [-B minbinsize]", 347 "report information about malloc()s by cache", 348 umem_malloc_info, umem_malloc_info_help }, 349 { "umem_verify", "?", "check integrity of umem-managed memory", 350 umem_verify }, 351 { "vmem", "?", "print a vmem_t", vmem }, 352 { "vmem_seg", ":[-sv] [-c caller] [-e earliest] [-l latest] " 353 "[-m minsize] [-M maxsize] [-t thread] [-T type]", 354 "print or filter a vmem_seg", vmem_seg, vmem_seg_help }, 355 356 #ifndef _KMDB 357 /* from ../genunix/kgrep.c + libumem.c */ 358 { "ugrep", KGREP_USAGE, "search user address space for a pointer", 359 kgrep, kgrep_help }, 360 361 /* from ../genunix/leaky.c + leaky_subr.c */ 362 { "findleaks", FINDLEAKS_USAGE, "search for potential memory leaks", 363 findleaks, findleaks_help }, 364 #endif 365 366 { NULL } 367 }; 368 369 static const mdb_walker_t walkers[] = { 370 371 /* from umem.c */ 372 { "allocdby", "given a thread, walk its allocated bufctls", 373 allocdby_walk_init, allocdby_walk_step, allocdby_walk_fini }, 374 { "bufctl", "walk a umem cache's bufctls", 375 bufctl_walk_init, umem_walk_step, umem_walk_fini }, 376 { "bufctl_history", "walk the available history of a bufctl", 377 bufctl_history_walk_init, bufctl_history_walk_step, 378 bufctl_history_walk_fini }, 379 { "freectl", "walk a umem cache's free bufctls", 380 freectl_walk_init, umem_walk_step, umem_walk_fini }, 381 { "freedby", "given a thread, walk its freed bufctls", 382 freedby_walk_init, allocdby_walk_step, allocdby_walk_fini }, 383 { "freemem", "walk a umem cache's free memory", 384 freemem_walk_init, umem_walk_step, umem_walk_fini }, 385 { "umem", "walk a umem cache", 386 umem_walk_init, umem_walk_step, umem_walk_fini }, 387 { "umem_cpu", "walk the umem CPU structures", 388 umem_cpu_walk_init, umem_cpu_walk_step, umem_cpu_walk_fini }, 389 { "umem_cpu_cache", "given a umem cache, walk its per-CPU caches", 390 umem_cpu_cache_walk_init, umem_cpu_cache_walk_step, NULL }, 391 { "umem_hash", "given a umem cache, walk its allocated hash table", 392 umem_hash_walk_init, umem_hash_walk_step, umem_hash_walk_fini }, 393 { "umem_log", "walk the umem transaction log", 394 umem_log_walk_init, umem_log_walk_step, umem_log_walk_fini }, 395 { "umem_slab", "given a umem cache, walk its slabs", 396 umem_slab_walk_init, umem_slab_walk_step, NULL }, 397 { "umem_slab_partial", 398 "given a umem cache, walk its partially allocated slabs (min 1)", 399 umem_slab_walk_partial_init, umem_slab_walk_step, NULL }, 400 { "vmem", "walk vmem structures in pre-fix, depth-first order", 401 vmem_walk_init, vmem_walk_step, vmem_walk_fini }, 402 { "vmem_alloc", "given a vmem_t, walk its allocated vmem_segs", 403 vmem_alloc_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini }, 404 { "vmem_free", "given a vmem_t, walk its free vmem_segs", 405 vmem_free_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini }, 406 { "vmem_postfix", "walk vmem structures in post-fix, depth-first order", 407 vmem_walk_init, vmem_postfix_walk_step, vmem_walk_fini }, 408 { "vmem_seg", "given a vmem_t, walk all of its vmem_segs", 409 vmem_seg_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini }, 410 { "vmem_span", "given a vmem_t, walk its spanning vmem_segs", 411 vmem_span_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini }, 412 413 #ifndef _KMDB 414 /* from ../genunix/leaky.c + leaky_subr.c */ 415 { "leak", "given a leak ctl, walk other leaks w/ that stacktrace", 416 leaky_walk_init, leaky_walk_step, leaky_walk_fini }, 417 { "leakbuf", "given a leak ctl, walk addr of leaks w/ that stacktrace", 418 leaky_walk_init, leaky_buf_walk_step, leaky_walk_fini }, 419 #endif 420 421 { NULL } 422 }; 423 424 static const mdb_modinfo_t modinfo = {MDB_API_VERSION, dcmds, walkers}; 425 426 const mdb_modinfo_t * 427 _mdb_init(void) 428 { 429 if (umem_init() != 0) 430 return (NULL); 431 432 return (&modinfo); 433 } 434 435 void 436 _mdb_fini(void) 437 { 438 #ifndef _KMDB 439 leaky_cleanup(1); 440 #endif 441 } 442