dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / mdb / common / modules / libumem / libumem.c
blobeeba8b845655f641dfdcf7c5ec9d067193a9b6c7
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
30 #include "umem.h"
31 #include <libproc.h>
32 #include <mdb/mdb_modapi.h>
34 #include "kgrep.h"
35 #include "leaky.h"
36 #include "misc.h"
37 #include "proc_kludges.h"
39 #include <umem_impl.h>
40 #include <sys/vmem_impl_user.h>
41 #include <thr_uberdata.h>
43 #include "umem_pagesize.h"
45 typedef struct datafmt {
46 char *hdr1;
47 char *hdr2;
48 char *dashes;
49 char *fmt;
50 } datafmt_t;
52 static datafmt_t ptcfmt[] = {
53 { " ", "tid", "---", "%3u " },
54 { " memory", " cached", "-------", "%7lH " },
55 { " %", "cap", "---", "%3u " },
56 { " %", NULL, "---", "%3u " },
57 { NULL, NULL, NULL, NULL }
60 static datafmt_t umemfmt[] = {
61 { "cache ", "name ",
62 "-------------------------", "%-25s " },
63 { " buf", " size", "------", "%6u " },
64 { " buf", " in use", "-------", "%7u " },
65 { " buf", " in ptc", "-------", "%7s " },
66 { " buf", " total", "-------", "%7u " },
67 { " memory", " in use", "-------", "%7H " },
68 { " alloc", " succeed", "---------", "%9u " },
69 { "alloc", " fail", "-----", "%5llu" },
70 { NULL, NULL, NULL, NULL }
73 static datafmt_t vmemfmt[] = {
74 { "vmem ", "name ",
75 "-------------------------", "%-*s " },
76 { " memory", " in use", "---------", "%9H " },
77 { " memory", " total", "----------", "%10H " },
78 { " memory", " import", "---------", "%9H " },
79 { " alloc", " succeed", "---------", "%9llu " },
80 { "alloc", " fail", "-----", "%5llu " },
81 { NULL, NULL, NULL, NULL }
84 /*ARGSUSED*/
85 static int
86 umastat_cpu_avail(uintptr_t addr, const umem_cpu_cache_t *ccp, int *avail)
88 if (ccp->cc_rounds > 0)
89 *avail += ccp->cc_rounds;
90 if (ccp->cc_prounds > 0)
91 *avail += ccp->cc_prounds;
93 return (WALK_NEXT);
96 /*ARGSUSED*/
97 static int
98 umastat_cpu_alloc(uintptr_t addr, const umem_cpu_cache_t *ccp, int *alloc)
100 *alloc += ccp->cc_alloc;
102 return (WALK_NEXT);
105 /*ARGSUSED*/
106 static int
107 umastat_slab_avail(uintptr_t addr, const umem_slab_t *sp, int *avail)
109 *avail += sp->slab_chunks - sp->slab_refcnt;
111 return (WALK_NEXT);
114 typedef struct umastat_vmem {
115 uintptr_t kv_addr;
116 struct umastat_vmem *kv_next;
117 int kv_meminuse;
118 int kv_alloc;
119 int kv_fail;
120 } umastat_vmem_t;
122 /*ARGSUSED*/
123 static int
124 umastat_cache_nptc(uintptr_t addr, const umem_cache_t *cp, int *nptc)
126 if (!(cp->cache_flags & UMF_PTC))
127 return (WALK_NEXT);
129 (*nptc)++;
130 return (WALK_NEXT);
133 /*ARGSUSED*/
134 static int
135 umastat_cache_hdr(uintptr_t addr, const umem_cache_t *cp, void *ignored)
137 if (!(cp->cache_flags & UMF_PTC))
138 return (WALK_NEXT);
140 mdb_printf("%3d ", cp->cache_bufsize);
141 return (WALK_NEXT);
144 /*ARGSUSED*/
145 static int
146 umastat_lwp_ptc(uintptr_t addr, void *buf, int *nbufs)
148 (*nbufs)++;
149 return (WALK_NEXT);
152 /*ARGSUSED*/
153 static int
154 umastat_lwp_cache(uintptr_t addr, const umem_cache_t *cp, ulwp_t *ulwp)
156 char walk[60];
157 int nbufs = 0;
159 if (!(cp->cache_flags & UMF_PTC))
160 return (WALK_NEXT);
162 (void) mdb_snprintf(walk, sizeof (walk), "umem_ptc_%d",
163 cp->cache_bufsize);
165 if (mdb_pwalk(walk, (mdb_walk_cb_t)umastat_lwp_ptc,
166 &nbufs, (uintptr_t)ulwp->ul_self) == -1) {
167 mdb_warn("unable to walk '%s'", walk);
168 return (WALK_ERR);
171 mdb_printf("%3d ", ulwp->ul_tmem.tm_size ?
172 (nbufs * cp->cache_bufsize * 100) / ulwp->ul_tmem.tm_size : 0);
174 return (WALK_NEXT);
177 /*ARGSUSED*/
178 static int
179 umastat_lwp(uintptr_t addr, const ulwp_t *ulwp, void *ignored)
181 size_t size;
182 datafmt_t *dfp = ptcfmt;
184 mdb_printf((dfp++)->fmt, ulwp->ul_lwpid);
185 mdb_printf((dfp++)->fmt, ulwp->ul_tmem.tm_size);
187 if (umem_readvar(&size, "umem_ptc_size") == -1) {
188 mdb_warn("unable to read 'umem_ptc_size'");
189 return (WALK_ERR);
192 mdb_printf((dfp++)->fmt, (ulwp->ul_tmem.tm_size * 100) / size);
194 if (mdb_walk("umem_cache",
195 (mdb_walk_cb_t)umastat_lwp_cache, (void *)ulwp) == -1) {
196 mdb_warn("can't walk 'umem_cache'");
197 return (WALK_ERR);
200 mdb_printf("\n");
202 return (WALK_NEXT);
205 /*ARGSUSED*/
206 static int
207 umastat_cache_ptc(uintptr_t addr, const void *ignored, int *nptc)
209 (*nptc)++;
210 return (WALK_NEXT);
213 static int
214 umastat_cache(uintptr_t addr, const umem_cache_t *cp, umastat_vmem_t **kvp)
216 umastat_vmem_t *kv;
217 datafmt_t *dfp = umemfmt;
218 char buf[10];
219 int magsize;
221 int avail, alloc, total, nptc = 0;
222 size_t meminuse = (cp->cache_slab_create - cp->cache_slab_destroy) *
223 cp->cache_slabsize;
225 mdb_walk_cb_t cpu_avail = (mdb_walk_cb_t)umastat_cpu_avail;
226 mdb_walk_cb_t cpu_alloc = (mdb_walk_cb_t)umastat_cpu_alloc;
227 mdb_walk_cb_t slab_avail = (mdb_walk_cb_t)umastat_slab_avail;
229 magsize = umem_get_magsize(cp);
231 alloc = cp->cache_slab_alloc + cp->cache_full.ml_alloc;
232 avail = cp->cache_full.ml_total * magsize;
233 total = cp->cache_buftotal;
235 (void) mdb_pwalk("umem_cpu_cache", cpu_alloc, &alloc, addr);
236 (void) mdb_pwalk("umem_cpu_cache", cpu_avail, &avail, addr);
237 (void) mdb_pwalk("umem_slab_partial", slab_avail, &avail, addr);
239 if (cp->cache_flags & UMF_PTC) {
240 char walk[60];
242 (void) mdb_snprintf(walk, sizeof (walk),
243 "umem_ptc_%d", cp->cache_bufsize);
245 if (mdb_walk(walk,
246 (mdb_walk_cb_t)umastat_cache_ptc, &nptc) == -1) {
247 mdb_warn("unable to walk '%s'", walk);
248 return (WALK_ERR);
251 (void) mdb_snprintf(buf, sizeof (buf), "%d", nptc);
254 for (kv = *kvp; kv != NULL; kv = kv->kv_next) {
255 if (kv->kv_addr == (uintptr_t)cp->cache_arena)
256 goto out;
259 kv = mdb_zalloc(sizeof (umastat_vmem_t), UM_SLEEP | UM_GC);
260 kv->kv_next = *kvp;
261 kv->kv_addr = (uintptr_t)cp->cache_arena;
262 *kvp = kv;
263 out:
264 kv->kv_meminuse += meminuse;
265 kv->kv_alloc += alloc;
266 kv->kv_fail += cp->cache_alloc_fail;
268 mdb_printf((dfp++)->fmt, cp->cache_name);
269 mdb_printf((dfp++)->fmt, cp->cache_bufsize);
270 mdb_printf((dfp++)->fmt, total - avail);
271 mdb_printf((dfp++)->fmt, cp->cache_flags & UMF_PTC ? buf : "-");
272 mdb_printf((dfp++)->fmt, total);
273 mdb_printf((dfp++)->fmt, meminuse);
274 mdb_printf((dfp++)->fmt, alloc);
275 mdb_printf((dfp++)->fmt, cp->cache_alloc_fail);
276 mdb_printf("\n");
278 return (WALK_NEXT);
281 static int
282 umastat_vmem_totals(uintptr_t addr, const vmem_t *v, umastat_vmem_t *kv)
284 while (kv != NULL && kv->kv_addr != addr)
285 kv = kv->kv_next;
287 if (kv == NULL || kv->kv_alloc == 0)
288 return (WALK_NEXT);
290 mdb_printf("Total [%s]%*s %6s %7s %7s %7s %7H %9u %5u\n", v->vm_name,
291 17 - strlen(v->vm_name), "", "", "", "", "",
292 kv->kv_meminuse, kv->kv_alloc, kv->kv_fail);
294 return (WALK_NEXT);
297 /*ARGSUSED*/
298 static int
299 umastat_vmem(uintptr_t addr, const vmem_t *v, void *ignored)
301 datafmt_t *dfp = vmemfmt;
302 uintptr_t paddr;
303 vmem_t parent;
304 int ident = 0;
306 for (paddr = (uintptr_t)v->vm_source; paddr != (uintptr_t)NULL;
307 ident += 4) {
308 if (mdb_vread(&parent, sizeof (parent), paddr) == -1) {
309 mdb_warn("couldn't trace %p's ancestry", addr);
310 ident = 0;
311 break;
313 paddr = (uintptr_t)parent.vm_source;
316 mdb_printf("%*s", ident, "");
317 mdb_printf((dfp++)->fmt, 25 - ident, v->vm_name);
318 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_inuse);
319 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_total);
320 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_import);
321 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_alloc);
322 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_fail);
324 mdb_printf("\n");
326 return (WALK_NEXT);
329 /*ARGSUSED*/
331 umastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
333 umastat_vmem_t *kv = NULL;
334 datafmt_t *dfp;
335 int nptc = 0, i;
337 if (argc != 0)
338 return (DCMD_USAGE);
341 * We need to determine if we have any caches that have per-thread
342 * caching enabled.
344 if (mdb_walk("umem_cache",
345 (mdb_walk_cb_t)umastat_cache_nptc, &nptc) == -1) {
346 mdb_warn("can't walk 'umem_cache'");
347 return (DCMD_ERR);
350 if (nptc) {
351 for (dfp = ptcfmt; dfp->hdr2 != NULL; dfp++)
352 mdb_printf("%s ", dfp->hdr1);
354 for (i = 0; i < nptc; i++)
355 mdb_printf("%s ", dfp->hdr1);
357 mdb_printf("\n");
359 for (dfp = ptcfmt; dfp->hdr2 != NULL; dfp++)
360 mdb_printf("%s ", dfp->hdr2);
362 if (mdb_walk("umem_cache",
363 (mdb_walk_cb_t)umastat_cache_hdr, NULL) == -1) {
364 mdb_warn("can't walk 'umem_cache'");
365 return (DCMD_ERR);
368 mdb_printf("\n");
370 for (dfp = ptcfmt; dfp->hdr2 != NULL; dfp++)
371 mdb_printf("%s ", dfp->dashes);
373 for (i = 0; i < nptc; i++)
374 mdb_printf("%s ", dfp->dashes);
376 mdb_printf("\n");
378 if (mdb_walk("ulwp", (mdb_walk_cb_t)umastat_lwp, NULL) == -1) {
379 mdb_warn("can't walk 'ulwp'");
380 return (DCMD_ERR);
383 mdb_printf("\n");
386 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
387 mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->hdr1);
388 mdb_printf("\n");
390 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
391 mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->hdr2);
392 mdb_printf("\n");
394 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
395 mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->dashes);
396 mdb_printf("\n");
398 if (mdb_walk("umem_cache", (mdb_walk_cb_t)umastat_cache, &kv) == -1) {
399 mdb_warn("can't walk 'umem_cache'");
400 return (DCMD_ERR);
403 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
404 mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->dashes);
405 mdb_printf("\n");
407 if (mdb_walk("vmem", (mdb_walk_cb_t)umastat_vmem_totals, kv) == -1) {
408 mdb_warn("can't walk 'vmem'");
409 return (DCMD_ERR);
412 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
413 mdb_printf("%s ", dfp->dashes);
414 mdb_printf("\n");
416 mdb_printf("\n");
418 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
419 mdb_printf("%s ", dfp->hdr1);
420 mdb_printf("\n");
422 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
423 mdb_printf("%s ", dfp->hdr2);
424 mdb_printf("\n");
426 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
427 mdb_printf("%s ", dfp->dashes);
428 mdb_printf("\n");
430 if (mdb_walk("vmem", (mdb_walk_cb_t)umastat_vmem, NULL) == -1) {
431 mdb_warn("can't walk 'vmem'");
432 return (DCMD_ERR);
435 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
436 mdb_printf("%s ", dfp->dashes);
437 mdb_printf("\n");
438 return (DCMD_OK);
442 * kmdb doesn't use libproc, and thus doesn't have any prmap_t's to walk.
443 * We have other ways to grep kmdb's address range.
445 #ifndef _KMDB
447 typedef struct ugrep_walk_data {
448 kgrep_cb_func *ug_cb;
449 void *ug_cbdata;
450 } ugrep_walk_data_t;
452 /*ARGSUSED*/
454 ugrep_mapping_cb(uintptr_t addr, const void *prm_arg, void *data)
456 ugrep_walk_data_t *ug = data;
457 const prmap_t *prm = prm_arg;
459 return (ug->ug_cb(prm->pr_vaddr, prm->pr_vaddr + prm->pr_size,
460 ug->ug_cbdata));
464 kgrep_subr(kgrep_cb_func *cb, void *cbdata)
466 ugrep_walk_data_t ug;
468 prockludge_add_walkers();
470 ug.ug_cb = cb;
471 ug.ug_cbdata = cbdata;
473 if (mdb_walk(KLUDGE_MAPWALK_NAME, ugrep_mapping_cb, &ug) == -1) {
474 mdb_warn("Unable to walk "KLUDGE_MAPWALK_NAME);
475 return (DCMD_ERR);
478 prockludge_remove_walkers();
479 return (DCMD_OK);
482 size_t
483 kgrep_subr_pagesize(void)
485 return (PAGESIZE);
488 #endif /* !_KMDB */
490 static const mdb_dcmd_t dcmds[] = {
492 /* from libumem.c */
493 { "umastat", NULL, "umem allocator stats", umastat },
495 /* from misc.c */
496 { "umem_debug", NULL, "toggle umem dcmd/walk debugging", umem_debug},
498 /* from umem.c */
499 { "umem_status", NULL, "Print umem status and message buffer",
500 umem_status },
501 { "allocdby", ":", "given a thread, print its allocated buffers",
502 allocdby },
503 { "bufctl", ":[-vh] [-a addr] [-c caller] [-e earliest] [-l latest] "
504 "[-t thd]", "print or filter a bufctl", bufctl, bufctl_help },
505 { "bufctl_audit", ":", "print a bufctl_audit", bufctl_audit },
506 { "freedby", ":", "given a thread, print its freed buffers", freedby },
507 { "umalog", "[ fail | slab ]",
508 "display umem transaction log and stack traces", umalog },
509 { "umausers", "[-ef] [cache ...]", "display current medium and large "
510 "users of the umem allocator", umausers },
511 { "umem_cache", "?", "print a umem cache", umem_cache },
512 { "umem_log", "?", "dump umem transaction log", umem_log },
513 { "umem_malloc_dist", "[-dg] [-b maxbins] [-B minbinsize]",
514 "report distribution of outstanding malloc()s",
515 umem_malloc_dist, umem_malloc_dist_help },
516 { "umem_malloc_info", "?[-dg] [-b maxbins] [-B minbinsize]",
517 "report information about malloc()s by cache",
518 umem_malloc_info, umem_malloc_info_help },
519 { "umem_verify", "?", "check integrity of umem-managed memory",
520 umem_verify },
521 { "vmem", "?", "print a vmem_t", vmem },
522 { "vmem_seg", ":[-sv] [-c caller] [-e earliest] [-l latest] "
523 "[-m minsize] [-M maxsize] [-t thread] [-T type]",
524 "print or filter a vmem_seg", vmem_seg, vmem_seg_help },
526 #ifndef _KMDB
527 /* from ../genunix/kgrep.c + libumem.c */
528 { "ugrep", KGREP_USAGE, "search user address space for a pointer",
529 kgrep, kgrep_help },
531 /* from ../genunix/leaky.c + leaky_subr.c */
532 { "findleaks", FINDLEAKS_USAGE, "search for potential memory leaks",
533 findleaks, findleaks_help },
534 #endif
536 { NULL }
539 static const mdb_walker_t walkers[] = {
541 /* from umem.c */
542 { "allocdby", "given a thread, walk its allocated bufctls",
543 allocdby_walk_init, allocdby_walk_step, allocdby_walk_fini },
544 { "bufctl", "walk a umem cache's bufctls",
545 bufctl_walk_init, umem_walk_step, umem_walk_fini },
546 { "bufctl_history", "walk the available history of a bufctl",
547 bufctl_history_walk_init, bufctl_history_walk_step,
548 bufctl_history_walk_fini },
549 { "freectl", "walk a umem cache's free bufctls",
550 freectl_walk_init, umem_walk_step, umem_walk_fini },
551 { "freedby", "given a thread, walk its freed bufctls",
552 freedby_walk_init, allocdby_walk_step, allocdby_walk_fini },
553 { "freemem", "walk a umem cache's free memory",
554 freemem_walk_init, umem_walk_step, umem_walk_fini },
555 { "umem", "walk a umem cache",
556 umem_walk_init, umem_walk_step, umem_walk_fini },
557 { "umem_cpu", "walk the umem CPU structures",
558 umem_cpu_walk_init, umem_cpu_walk_step, umem_cpu_walk_fini },
559 { "umem_cpu_cache", "given a umem cache, walk its per-CPU caches",
560 umem_cpu_cache_walk_init, umem_cpu_cache_walk_step, NULL },
561 { "umem_hash", "given a umem cache, walk its allocated hash table",
562 umem_hash_walk_init, umem_hash_walk_step, umem_hash_walk_fini },
563 { "umem_log", "walk the umem transaction log",
564 umem_log_walk_init, umem_log_walk_step, umem_log_walk_fini },
565 { "umem_slab", "given a umem cache, walk its slabs",
566 umem_slab_walk_init, umem_slab_walk_step, NULL },
567 { "umem_slab_partial",
568 "given a umem cache, walk its partially allocated slabs (min 1)",
569 umem_slab_walk_partial_init, umem_slab_walk_step, NULL },
570 { "vmem", "walk vmem structures in pre-fix, depth-first order",
571 vmem_walk_init, vmem_walk_step, vmem_walk_fini },
572 { "vmem_alloc", "given a vmem_t, walk its allocated vmem_segs",
573 vmem_alloc_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
574 { "vmem_free", "given a vmem_t, walk its free vmem_segs",
575 vmem_free_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
576 { "vmem_postfix", "walk vmem structures in post-fix, depth-first order",
577 vmem_walk_init, vmem_postfix_walk_step, vmem_walk_fini },
578 { "vmem_seg", "given a vmem_t, walk all of its vmem_segs",
579 vmem_seg_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
580 { "vmem_span", "given a vmem_t, walk its spanning vmem_segs",
581 vmem_span_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
583 #ifndef _KMDB
584 /* from ../genunix/leaky.c + leaky_subr.c */
585 { "leak", "given a leak ctl, walk other leaks w/ that stacktrace",
586 leaky_walk_init, leaky_walk_step, leaky_walk_fini },
587 { "leakbuf", "given a leak ctl, walk addr of leaks w/ that stacktrace",
588 leaky_walk_init, leaky_buf_walk_step, leaky_walk_fini },
589 #endif
591 { NULL }
594 static const mdb_modinfo_t modinfo = {MDB_API_VERSION, dcmds, walkers};
596 const mdb_modinfo_t *
597 _mdb_init(void)
599 if (umem_init() != 0)
600 return (NULL);
602 return (&modinfo);
605 void
606 _mdb_fini(void)
608 #ifndef _KMDB
609 leaky_cleanup(1);
610 #endif