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
!= (uintptr_t)NULL
;
308 if (mdb_vread(&parent
, sizeof (parent
), paddr
) == -1) {
309 mdb_warn("couldn't trace %p's ancestry", addr
);
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
);
331 umastat(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
333 umastat_vmem_t
*kv
= NULL
;
341 * We need to determine if we have any caches that have per-thread
344 if (mdb_walk("umem_cache",
345 (mdb_walk_cb_t
)umastat_cache_nptc
, &nptc
) == -1) {
346 mdb_warn("can't walk 'umem_cache'");
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
);
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'");
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
);
378 if (mdb_walk("ulwp", (mdb_walk_cb_t
)umastat_lwp
, NULL
) == -1) {
379 mdb_warn("can't walk 'ulwp'");
386 for (dfp
= umemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
387 mdb_printf("%s%s", dfp
== umemfmt
? "" : " ", dfp
->hdr1
);
390 for (dfp
= umemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
391 mdb_printf("%s%s", dfp
== umemfmt
? "" : " ", dfp
->hdr2
);
394 for (dfp
= umemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
395 mdb_printf("%s%s", dfp
== umemfmt
? "" : " ", dfp
->dashes
);
398 if (mdb_walk("umem_cache", (mdb_walk_cb_t
)umastat_cache
, &kv
) == -1) {
399 mdb_warn("can't walk 'umem_cache'");
403 for (dfp
= umemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
404 mdb_printf("%s%s", dfp
== umemfmt
? "" : " ", dfp
->dashes
);
407 if (mdb_walk("vmem", (mdb_walk_cb_t
)umastat_vmem_totals
, kv
) == -1) {
408 mdb_warn("can't walk 'vmem'");
412 for (dfp
= umemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
413 mdb_printf("%s ", dfp
->dashes
);
418 for (dfp
= vmemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
419 mdb_printf("%s ", dfp
->hdr1
);
422 for (dfp
= vmemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
423 mdb_printf("%s ", dfp
->hdr2
);
426 for (dfp
= vmemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
427 mdb_printf("%s ", dfp
->dashes
);
430 if (mdb_walk("vmem", (mdb_walk_cb_t
)umastat_vmem
, NULL
) == -1) {
431 mdb_warn("can't walk 'vmem'");
435 for (dfp
= vmemfmt
; dfp
->hdr1
!= NULL
; dfp
++)
436 mdb_printf("%s ", dfp
->dashes
);
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.
447 typedef struct ugrep_walk_data
{
448 kgrep_cb_func
*ug_cb
;
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
,
464 kgrep_subr(kgrep_cb_func
*cb
, void *cbdata
)
466 ugrep_walk_data_t ug
;
468 prockludge_add_walkers();
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
);
478 prockludge_remove_walkers();
483 kgrep_subr_pagesize(void)
490 static const mdb_dcmd_t dcmds
[] = {
493 { "umastat", NULL
, "umem allocator stats", umastat
},
496 { "umem_debug", NULL
, "toggle umem dcmd/walk debugging", umem_debug
},
499 { "umem_status", NULL
, "Print umem status and message buffer",
501 { "allocdby", ":", "given a thread, print its allocated buffers",
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",
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
},
527 /* from ../genunix/kgrep.c + libumem.c */
528 { "ugrep", KGREP_USAGE
, "search user address space for a pointer",
531 /* from ../genunix/leaky.c + leaky_subr.c */
532 { "findleaks", FINDLEAKS_USAGE
, "search for potential memory leaks",
533 findleaks
, findleaks_help
},
539 static const mdb_walker_t walkers
[] = {
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
},
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
},
594 static const mdb_modinfo_t modinfo
= {MDB_API_VERSION
, dcmds
, walkers
};
596 const mdb_modinfo_t
*
599 if (umem_init() != 0)