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]
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.
32 #include <mdb/mdb_modapi.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
{
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
[] = {
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
[] = {
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
}
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
;
98 umastat_cpu_alloc(uintptr_t addr
, const umem_cpu_cache_t
*ccp
, int *alloc
)
100 *alloc
+= ccp
->cc_alloc
;
107 umastat_slab_avail(uintptr_t addr
, const umem_slab_t
*sp
, int *avail
)
109 *avail
+= sp
->slab_chunks
- sp
->slab_refcnt
;
114 typedef struct umastat_vmem
{
116 struct umastat_vmem
*kv_next
;
124 umastat_cache_nptc(uintptr_t addr
, const umem_cache_t
*cp
, int *nptc
)
126 if (!(cp
->cache_flags
& UMF_PTC
))
135 umastat_cache_hdr(uintptr_t addr
, const umem_cache_t
*cp
, void *ignored
)
137 if (!(cp
->cache_flags
& UMF_PTC
))
140 mdb_printf("%3d ", cp
->cache_bufsize
);
146 umastat_lwp_ptc(uintptr_t addr
, void *buf
, int *nbufs
)
154 umastat_lwp_cache(uintptr_t addr
, const umem_cache_t
*cp
, ulwp_t
*ulwp
)
159 if (!(cp
->cache_flags
& UMF_PTC
))
162 (void) mdb_snprintf(walk
, sizeof (walk
), "umem_ptc_%d",
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
);
171 mdb_printf("%3d ", ulwp
->ul_tmem
.tm_size
?
172 (nbufs
* cp
->cache_bufsize
* 100) / ulwp
->ul_tmem
.tm_size
: 0);
179 umastat_lwp(uintptr_t addr
, const ulwp_t
*ulwp
, void *ignored
)
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'");
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'");
207 umastat_cache_ptc(uintptr_t addr
, const void *ignored
, int *nptc
)
214 umastat_cache(uintptr_t addr
, const umem_cache_t
*cp
, umastat_vmem_t
**kvp
)
217 datafmt_t
*dfp
= umemfmt
;
221 int avail
, alloc
, total
, nptc
= 0;
222 size_t meminuse
= (cp
->cache_slab_create
- cp
->cache_slab_destroy
) *
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
) {
242 (void) mdb_snprintf(walk
, sizeof (walk
),
243 "umem_ptc_%d", cp
->cache_bufsize
);
246 (mdb_walk_cb_t
)umastat_cache_ptc
, &nptc
) == -1) {
247 mdb_warn("unable to walk '%s'", walk
);
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
)
259 kv
= mdb_zalloc(sizeof (umastat_vmem_t
), UM_SLEEP
| UM_GC
);
261 kv
->kv_addr
= (uintptr_t)cp
->cache_arena
;
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
);
282 umastat_vmem_totals(uintptr_t addr
, const vmem_t
*v
, umastat_vmem_t
*kv
)
284 while (kv
!= NULL
&& kv
->kv_addr
!= addr
)
287 if (kv
== NULL
|| kv
->kv_alloc
== 0)
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
);
299 umastat_vmem(uintptr_t addr
, const vmem_t
*v
, void *ignored
)
301 datafmt_t
*dfp
= vmemfmt
;
306 for (paddr
= (uintptr_t)v
->vm_source
; paddr
!= NULL
; ident
+= 4) {
307 if (mdb_vread(&parent
, sizeof (parent
), paddr
) == -1) {
308 mdb_warn("couldn't trace %p's ancestry", addr
);
312 paddr
= (uintptr_t)parent
.vm_source
;
315 mdb_printf("%*s", ident
, "");
316 mdb_printf((dfp
++)->fmt
, 25 - ident
, v
->vm_name
);
317 mdb_printf((dfp
++)->fmt
, v
->vm_kstat
.vk_mem_inuse
);
318 mdb_printf((dfp
++)->fmt
, v
->vm_kstat
.vk_mem_total
);
319 mdb_printf((dfp
++)->fmt
, v
->vm_kstat
.vk_mem_import
);
320 mdb_printf((dfp
++)->fmt
, v
->vm_kstat
.vk_alloc
);
321 mdb_printf((dfp
++)->fmt
, v
->vm_kstat
.vk_fail
);
330 umastat(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
332 umastat_vmem_t
*kv
= NULL
;
340 * We need to determine if we have any caches that have per-thread
343 if (mdb_walk("umem_cache",
344 (mdb_walk_cb_t
)umastat_cache_nptc
, &nptc
) == -1) {
345 mdb_warn("can't walk 'umem_cache'");
350 for (dfp
= ptcfmt
; dfp
->hdr2
!= NULL
; dfp
++)
351 mdb_printf("%s ", dfp
->hdr1
);
353 for (i
= 0; i
< nptc
; i
++)
354 mdb_printf("%s ", dfp
->hdr1
);
358 for (dfp
= ptcfmt
; dfp
->hdr2
!= NULL
; dfp
++)
359 mdb_printf("%s ", dfp
->hdr2
);
361 if (mdb_walk("umem_cache",
362 (mdb_walk_cb_t
)umastat_cache_hdr
, NULL
) == -1) {
363 mdb_warn("can't walk 'umem_cache'");
369 for (dfp
= ptcfmt
; dfp
->hdr2
!= NULL
; dfp
++)
370 mdb_printf("%s ", dfp
->dashes
);
372 for (i
= 0; i
< nptc
; i
++)
373 mdb_printf("%s ", dfp
->dashes
);
377 if (mdb_walk("ulwp", (mdb_walk_cb_t
)umastat_lwp
, NULL
) == -1) {
378 mdb_warn("can't walk 'ulwp'");
385 for (dfp
= umemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
386 mdb_printf("%s%s", dfp
== umemfmt
? "" : " ", dfp
->hdr1
);
389 for (dfp
= umemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
390 mdb_printf("%s%s", dfp
== umemfmt
? "" : " ", dfp
->hdr2
);
393 for (dfp
= umemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
394 mdb_printf("%s%s", dfp
== umemfmt
? "" : " ", dfp
->dashes
);
397 if (mdb_walk("umem_cache", (mdb_walk_cb_t
)umastat_cache
, &kv
) == -1) {
398 mdb_warn("can't walk 'umem_cache'");
402 for (dfp
= umemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
403 mdb_printf("%s%s", dfp
== umemfmt
? "" : " ", dfp
->dashes
);
406 if (mdb_walk("vmem", (mdb_walk_cb_t
)umastat_vmem_totals
, kv
) == -1) {
407 mdb_warn("can't walk 'vmem'");
411 for (dfp
= umemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
412 mdb_printf("%s ", dfp
->dashes
);
417 for (dfp
= vmemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
418 mdb_printf("%s ", dfp
->hdr1
);
421 for (dfp
= vmemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
422 mdb_printf("%s ", dfp
->hdr2
);
425 for (dfp
= vmemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
426 mdb_printf("%s ", dfp
->dashes
);
429 if (mdb_walk("vmem", (mdb_walk_cb_t
)umastat_vmem
, NULL
) == -1) {
430 mdb_warn("can't walk 'vmem'");
434 for (dfp
= vmemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
435 mdb_printf("%s ", dfp
->dashes
);
441 * kmdb doesn't use libproc, and thus doesn't have any prmap_t's to walk.
442 * We have other ways to grep kmdb's address range.
446 typedef struct ugrep_walk_data
{
447 kgrep_cb_func
*ug_cb
;
453 ugrep_mapping_cb(uintptr_t addr
, const void *prm_arg
, void *data
)
455 ugrep_walk_data_t
*ug
= data
;
456 const prmap_t
*prm
= prm_arg
;
458 return (ug
->ug_cb(prm
->pr_vaddr
, prm
->pr_vaddr
+ prm
->pr_size
,
463 kgrep_subr(kgrep_cb_func
*cb
, void *cbdata
)
465 ugrep_walk_data_t ug
;
467 prockludge_add_walkers();
470 ug
.ug_cbdata
= cbdata
;
472 if (mdb_walk(KLUDGE_MAPWALK_NAME
, ugrep_mapping_cb
, &ug
) == -1) {
473 mdb_warn("Unable to walk "KLUDGE_MAPWALK_NAME
);
477 prockludge_remove_walkers();
482 kgrep_subr_pagesize(void)
489 static const mdb_dcmd_t dcmds
[] = {
492 { "umastat", NULL
, "umem allocator stats", umastat
},
495 { "umem_debug", NULL
, "toggle umem dcmd/walk debugging", umem_debug
},
498 { "umem_status", NULL
, "Print umem status and message buffer",
500 { "allocdby", ":", "given a thread, print its allocated buffers",
502 { "bufctl", ":[-vh] [-a addr] [-c caller] [-e earliest] [-l latest] "
503 "[-t thd]", "print or filter a bufctl", bufctl
, bufctl_help
},
504 { "bufctl_audit", ":", "print a bufctl_audit", bufctl_audit
},
505 { "freedby", ":", "given a thread, print its freed buffers", freedby
},
506 { "umalog", "[ fail | slab ]",
507 "display umem transaction log and stack traces", umalog
},
508 { "umausers", "[-ef] [cache ...]", "display current medium and large "
509 "users of the umem allocator", umausers
},
510 { "umem_cache", "?", "print a umem cache", umem_cache
},
511 { "umem_log", "?", "dump umem transaction log", umem_log
},
512 { "umem_malloc_dist", "[-dg] [-b maxbins] [-B minbinsize]",
513 "report distribution of outstanding malloc()s",
514 umem_malloc_dist
, umem_malloc_dist_help
},
515 { "umem_malloc_info", "?[-dg] [-b maxbins] [-B minbinsize]",
516 "report information about malloc()s by cache",
517 umem_malloc_info
, umem_malloc_info_help
},
518 { "umem_verify", "?", "check integrity of umem-managed memory",
520 { "vmem", "?", "print a vmem_t", vmem
},
521 { "vmem_seg", ":[-sv] [-c caller] [-e earliest] [-l latest] "
522 "[-m minsize] [-M maxsize] [-t thread] [-T type]",
523 "print or filter a vmem_seg", vmem_seg
, vmem_seg_help
},
526 /* from ../genunix/kgrep.c + libumem.c */
527 { "ugrep", KGREP_USAGE
, "search user address space for a pointer",
530 /* from ../genunix/leaky.c + leaky_subr.c */
531 { "findleaks", FINDLEAKS_USAGE
, "search for potential memory leaks",
532 findleaks
, findleaks_help
},
538 static const mdb_walker_t walkers
[] = {
541 { "allocdby", "given a thread, walk its allocated bufctls",
542 allocdby_walk_init
, allocdby_walk_step
, allocdby_walk_fini
},
543 { "bufctl", "walk a umem cache's bufctls",
544 bufctl_walk_init
, umem_walk_step
, umem_walk_fini
},
545 { "bufctl_history", "walk the available history of a bufctl",
546 bufctl_history_walk_init
, bufctl_history_walk_step
,
547 bufctl_history_walk_fini
},
548 { "freectl", "walk a umem cache's free bufctls",
549 freectl_walk_init
, umem_walk_step
, umem_walk_fini
},
550 { "freedby", "given a thread, walk its freed bufctls",
551 freedby_walk_init
, allocdby_walk_step
, allocdby_walk_fini
},
552 { "freemem", "walk a umem cache's free memory",
553 freemem_walk_init
, umem_walk_step
, umem_walk_fini
},
554 { "umem", "walk a umem cache",
555 umem_walk_init
, umem_walk_step
, umem_walk_fini
},
556 { "umem_cpu", "walk the umem CPU structures",
557 umem_cpu_walk_init
, umem_cpu_walk_step
, umem_cpu_walk_fini
},
558 { "umem_cpu_cache", "given a umem cache, walk its per-CPU caches",
559 umem_cpu_cache_walk_init
, umem_cpu_cache_walk_step
, NULL
},
560 { "umem_hash", "given a umem cache, walk its allocated hash table",
561 umem_hash_walk_init
, umem_hash_walk_step
, umem_hash_walk_fini
},
562 { "umem_log", "walk the umem transaction log",
563 umem_log_walk_init
, umem_log_walk_step
, umem_log_walk_fini
},
564 { "umem_slab", "given a umem cache, walk its slabs",
565 umem_slab_walk_init
, umem_slab_walk_step
, NULL
},
566 { "umem_slab_partial",
567 "given a umem cache, walk its partially allocated slabs (min 1)",
568 umem_slab_walk_partial_init
, umem_slab_walk_step
, NULL
},
569 { "vmem", "walk vmem structures in pre-fix, depth-first order",
570 vmem_walk_init
, vmem_walk_step
, vmem_walk_fini
},
571 { "vmem_alloc", "given a vmem_t, walk its allocated vmem_segs",
572 vmem_alloc_walk_init
, vmem_seg_walk_step
, vmem_seg_walk_fini
},
573 { "vmem_free", "given a vmem_t, walk its free vmem_segs",
574 vmem_free_walk_init
, vmem_seg_walk_step
, vmem_seg_walk_fini
},
575 { "vmem_postfix", "walk vmem structures in post-fix, depth-first order",
576 vmem_walk_init
, vmem_postfix_walk_step
, vmem_walk_fini
},
577 { "vmem_seg", "given a vmem_t, walk all of its vmem_segs",
578 vmem_seg_walk_init
, vmem_seg_walk_step
, vmem_seg_walk_fini
},
579 { "vmem_span", "given a vmem_t, walk its spanning vmem_segs",
580 vmem_span_walk_init
, vmem_seg_walk_step
, vmem_seg_walk_fini
},
583 /* from ../genunix/leaky.c + leaky_subr.c */
584 { "leak", "given a leak ctl, walk other leaks w/ that stacktrace",
585 leaky_walk_init
, leaky_walk_step
, leaky_walk_fini
},
586 { "leakbuf", "given a leak ctl, walk addr of leaks w/ that stacktrace",
587 leaky_walk_init
, leaky_buf_walk_step
, leaky_walk_fini
},
593 static const mdb_modinfo_t modinfo
= {MDB_API_VERSION
, dcmds
, walkers
};
595 const mdb_modinfo_t
*
598 if (umem_init() != 0)