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 2012 Joyent, Inc. All rights reserved.
28 * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
33 #include <sys/vmem_impl_user.h>
34 #include <umem_impl.h>
38 #include <mdb/mdb_whatis.h>
39 #include <thr_uberdata.h>
45 #include "umem_pagesize.h"
47 #define UM_ALLOCATED 0x1
54 static int umem_stack_depth_warned
;
55 static uint32_t umem_max_ncpus
;
56 uint32_t umem_stack_depth
;
60 #define UMEM_READVAR(var) \
61 (umem_readvar(&(var), #var) == -1 && \
62 (mdb_warn("failed to read "#var), 1))
65 umem_update_variables(void)
70 * Figure out which type of umem is being used; if it's not there
71 * yet, succeed quietly.
73 if (umem_set_standalone() == -1) {
75 return (0); /* umem not there yet */
79 * Solaris 9 used a different name for umem_max_ncpus. It's
80 * cheap backwards compatibility to check for both names.
82 if (umem_readvar(&umem_max_ncpus
, "umem_max_ncpus") == -1 &&
83 umem_readvar(&umem_max_ncpus
, "max_ncpus") == -1) {
84 mdb_warn("unable to read umem_max_ncpus or max_ncpus");
87 if (UMEM_READVAR(umem_ready
))
89 if (UMEM_READVAR(umem_stack_depth
))
91 if (UMEM_READVAR(pagesize
))
94 if (umem_stack_depth
> UMEM_MAX_STACK_DEPTH
) {
95 if (umem_stack_depth_warned
== 0) {
96 mdb_warn("umem_stack_depth corrupted (%d > %d)\n",
97 umem_stack_depth
, UMEM_MAX_STACK_DEPTH
);
98 umem_stack_depth_warned
= 1;
100 umem_stack_depth
= 0;
103 umem_pagesize
= pagesize
;
109 umem_ptc_walk_init(mdb_walk_state_t
*wsp
)
111 if (wsp
->walk_addr
== NULL
) {
112 if (mdb_layered_walk("ulwp", wsp
) == -1) {
113 mdb_warn("couldn't walk 'ulwp'");
122 umem_ptc_walk_step(mdb_walk_state_t
*wsp
)
127 if (wsp
->walk_layer
!= NULL
) {
128 this = (uintptr_t)((ulwp_t
*)wsp
->walk_layer
)->ul_self
+
129 (uintptr_t)wsp
->walk_arg
;
131 this = wsp
->walk_addr
+ (uintptr_t)wsp
->walk_arg
;
135 if (mdb_vread(&this, sizeof (void *), this) == -1) {
136 mdb_warn("couldn't read ptc buffer at %p", this);
143 rval
= wsp
->walk_callback(this, &this, wsp
->walk_cbdata
);
145 if (rval
!= WALK_NEXT
)
149 return (wsp
->walk_layer
!= NULL
? WALK_NEXT
: WALK_DONE
);
154 umem_init_walkers(uintptr_t addr
, const umem_cache_t
*c
, int *sizes
)
161 (void) mdb_snprintf(descr
, sizeof (descr
),
162 "walk the %s cache", c
->cache_name
);
164 w
.walk_name
= c
->cache_name
;
165 w
.walk_descr
= descr
;
166 w
.walk_init
= umem_walk_init
;
167 w
.walk_step
= umem_walk_step
;
168 w
.walk_fini
= umem_walk_fini
;
169 w
.walk_init_arg
= (void *)addr
;
171 if (mdb_add_walker(&w
) == -1)
172 mdb_warn("failed to add %s walker", c
->cache_name
);
174 if (!(c
->cache_flags
& UMF_PTC
))
178 * For the per-thread cache walker, the address is the offset in the
179 * tm_roots[] array of the ulwp_t.
181 for (i
= 0; sizes
[i
] != 0; i
++) {
182 if (sizes
[i
] == c
->cache_bufsize
)
187 mdb_warn("cache %s is cached per-thread, but could not find "
188 "size in umem_alloc_sizes\n", c
->cache_name
);
192 if (i
>= NTMEMBASE
) {
193 mdb_warn("index for %s (%d) exceeds root slots (%d)\n",
194 c
->cache_name
, i
, NTMEMBASE
);
198 (void) mdb_snprintf(name
, sizeof (name
),
199 "umem_ptc_%d", c
->cache_bufsize
);
200 (void) mdb_snprintf(descr
, sizeof (descr
),
201 "walk the per-thread cache for %s", c
->cache_name
);
204 w
.walk_descr
= descr
;
205 w
.walk_init
= umem_ptc_walk_init
;
206 w
.walk_step
= umem_ptc_walk_step
;
208 w
.walk_init_arg
= (void *)offsetof(ulwp_t
, ul_tmem
.tm_roots
[i
]);
210 if (mdb_add_walker(&w
) == -1)
211 mdb_warn("failed to add %s walker", w
.walk_name
);
218 umem_statechange_cb(void *arg
)
220 static int been_ready
= 0;
225 leaky_cleanup(1); /* state changes invalidate leaky state */
228 if (umem_update_variables() == -1)
234 if (umem_ready
!= UMEM_READY
)
240 * In order to determine the tm_roots offset of any cache that is
241 * cached per-thread, we need to have the umem_alloc_sizes array.
242 * Read this, assuring that it is zero-terminated.
244 if (umem_lookup_by_name("umem_alloc_sizes", &sym
) == -1) {
245 mdb_warn("unable to lookup 'umem_alloc_sizes'");
249 sizes
= mdb_zalloc(sym
.st_size
+ sizeof (int), UM_SLEEP
| UM_GC
);
251 if (mdb_vread(sizes
, sym
.st_size
, (uintptr_t)sym
.st_value
) == -1) {
252 mdb_warn("couldn't read 'umem_alloc_sizes'");
256 (void) mdb_walk("umem_cache", (mdb_walk_cb_t
)umem_init_walkers
, sizes
);
260 umem_abort_messages(void)
262 char *umem_error_buffer
;
263 uint_t umem_error_begin
;
267 if (UMEM_READVAR(umem_error_begin
))
270 if (umem_lookup_by_name("umem_error_buffer", &sym
) == -1) {
271 mdb_warn("unable to look up umem_error_buffer");
275 bufsize
= (size_t)sym
.st_size
;
277 umem_error_buffer
= mdb_alloc(bufsize
+1, UM_SLEEP
| UM_GC
);
279 if (mdb_vread(umem_error_buffer
, bufsize
, (uintptr_t)sym
.st_value
)
281 mdb_warn("unable to read umem_error_buffer");
284 /* put a zero after the end of the buffer to simplify printing */
285 umem_error_buffer
[bufsize
] = 0;
287 if ((umem_error_begin
% bufsize
) == 0)
288 mdb_printf("%s\n", umem_error_buffer
);
290 umem_error_buffer
[(umem_error_begin
% bufsize
) - 1] = 0;
292 &umem_error_buffer
[umem_error_begin
% bufsize
],
300 umem_log_status(const char *name
, umem_log_header_t
*val
)
302 umem_log_header_t my_lh
;
303 uintptr_t pos
= (uintptr_t)val
;
309 if (mdb_vread(&my_lh
, sizeof (umem_log_header_t
), pos
) == -1) {
310 mdb_warn("\nunable to read umem_%s_log pointer %p",
315 size
= my_lh
.lh_chunksize
* my_lh
.lh_nchunks
;
317 if (size
% (1024 * 1024) == 0)
318 mdb_printf("%s=%dm ", name
, size
/ (1024 * 1024));
319 else if (size
% 1024 == 0)
320 mdb_printf("%s=%dk ", name
, size
/ 1024);
322 mdb_printf("%s=%d ", name
, size
);
325 typedef struct umem_debug_flags
{
326 const char *udf_name
;
328 uint_t udf_clear
; /* if 0, uses udf_flags */
329 } umem_debug_flags_t
;
331 umem_debug_flags_t umem_status_flags
[] = {
332 { "random", UMF_RANDOMIZE
, UMF_RANDOM
},
333 { "default", UMF_AUDIT
| UMF_DEADBEEF
| UMF_REDZONE
| UMF_CONTENTS
},
334 { "audit", UMF_AUDIT
},
335 { "guards", UMF_DEADBEEF
| UMF_REDZONE
},
336 { "nosignal", UMF_CHECKSIGNAL
},
337 { "firewall", UMF_FIREWALL
},
338 { "lite", UMF_LITE
},
339 { "checknull", UMF_CHECKNULL
},
345 umem_status(uintptr_t addr
, uint_t flags
, int ac
, const mdb_arg_t
*argv
)
349 umem_log_header_t
*umem_transaction_log
;
350 umem_log_header_t
*umem_content_log
;
351 umem_log_header_t
*umem_failure_log
;
352 umem_log_header_t
*umem_slab_log
;
354 mdb_printf("Status:\t\t%s\n",
355 umem_ready
== UMEM_READY_INIT_FAILED
? "initialization failed" :
356 umem_ready
== UMEM_READY_STARTUP
? "uninitialized" :
357 umem_ready
== UMEM_READY_INITING
? "initialization in process" :
358 umem_ready
== UMEM_READY
? "ready and active" :
359 umem_ready
== 0 ? "not loaded into address space" :
360 "unknown (umem_ready invalid)");
365 mdb_printf("Concurrency:\t%d\n", umem_max_ncpus
);
367 if (UMEM_READVAR(umem_logging
))
369 if (UMEM_READVAR(umem_transaction_log
))
371 if (UMEM_READVAR(umem_content_log
))
373 if (UMEM_READVAR(umem_failure_log
))
375 if (UMEM_READVAR(umem_slab_log
))
378 mdb_printf("Logs:\t\t");
379 umem_log_status("transaction", umem_transaction_log
);
380 umem_log_status("content", umem_content_log
);
381 umem_log_status("fail", umem_failure_log
);
382 umem_log_status("slab", umem_slab_log
);
384 mdb_printf("(inactive)");
387 mdb_printf("Message buffer:\n");
388 return (umem_abort_messages());
391 mdb_printf("Message buffer:\n");
392 (void) umem_abort_messages();
398 uintptr_t ucw_current
;
402 umem_cache_walk_init(mdb_walk_state_t
*wsp
)
404 umem_cache_walk_t
*ucw
;
409 if (umem_lookup_by_name("umem_null_cache", &sym
) == -1) {
410 mdb_warn("couldn't find umem_null_cache");
414 cp
= (uintptr_t)sym
.st_value
;
416 if (mdb_vread(&c
, sizeof (umem_cache_t
), cp
) == -1) {
417 mdb_warn("couldn't read cache at %p", cp
);
421 ucw
= mdb_alloc(sizeof (umem_cache_walk_t
), UM_SLEEP
);
424 ucw
->ucw_current
= (uintptr_t)c
.cache_next
;
425 wsp
->walk_data
= ucw
;
431 umem_cache_walk_step(mdb_walk_state_t
*wsp
)
433 umem_cache_walk_t
*ucw
= wsp
->walk_data
;
437 if (mdb_vread(&c
, sizeof (umem_cache_t
), ucw
->ucw_current
) == -1) {
438 mdb_warn("couldn't read cache at %p", ucw
->ucw_current
);
442 status
= wsp
->walk_callback(ucw
->ucw_current
, &c
, wsp
->walk_cbdata
);
444 if ((ucw
->ucw_current
= (uintptr_t)c
.cache_next
) == ucw
->ucw_first
)
451 umem_cache_walk_fini(mdb_walk_state_t
*wsp
)
453 umem_cache_walk_t
*ucw
= wsp
->walk_data
;
454 mdb_free(ucw
, sizeof (umem_cache_walk_t
));
458 umem_cpu_t
*ucw_cpus
;
459 uint32_t ucw_current
;
461 } umem_cpu_walk_state_t
;
464 umem_cpu_walk_init(mdb_walk_state_t
*wsp
)
466 umem_cpu_t
*umem_cpus
;
468 umem_cpu_walk_state_t
*ucw
;
470 if (umem_readvar(&umem_cpus
, "umem_cpus") == -1) {
471 mdb_warn("failed to read 'umem_cpus'");
475 ucw
= mdb_alloc(sizeof (*ucw
), UM_SLEEP
);
477 ucw
->ucw_cpus
= umem_cpus
;
478 ucw
->ucw_current
= 0;
479 ucw
->ucw_max
= umem_max_ncpus
;
481 wsp
->walk_data
= ucw
;
486 umem_cpu_walk_step(mdb_walk_state_t
*wsp
)
489 umem_cpu_walk_state_t
*ucw
= wsp
->walk_data
;
493 if (ucw
->ucw_current
>= ucw
->ucw_max
)
496 caddr
= (uintptr_t)&(ucw
->ucw_cpus
[ucw
->ucw_current
]);
498 if (mdb_vread(&cpu
, sizeof (umem_cpu_t
), caddr
) == -1) {
499 mdb_warn("failed to read cpu %d", ucw
->ucw_current
);
505 return (wsp
->walk_callback(caddr
, &cpu
, wsp
->walk_cbdata
));
509 umem_cpu_walk_fini(mdb_walk_state_t
*wsp
)
511 umem_cpu_walk_state_t
*ucw
= wsp
->walk_data
;
513 mdb_free(ucw
, sizeof (*ucw
));
517 umem_cpu_cache_walk_init(mdb_walk_state_t
*wsp
)
519 if (wsp
->walk_addr
== NULL
) {
520 mdb_warn("umem_cpu_cache doesn't support global walks");
524 if (mdb_layered_walk("umem_cpu", wsp
) == -1) {
525 mdb_warn("couldn't walk 'umem_cpu'");
529 wsp
->walk_data
= (void *)wsp
->walk_addr
;
535 umem_cpu_cache_walk_step(mdb_walk_state_t
*wsp
)
537 uintptr_t caddr
= (uintptr_t)wsp
->walk_data
;
538 const umem_cpu_t
*cpu
= wsp
->walk_layer
;
541 caddr
+= cpu
->cpu_cache_offset
;
543 if (mdb_vread(&cc
, sizeof (umem_cpu_cache_t
), caddr
) == -1) {
544 mdb_warn("couldn't read umem_cpu_cache at %p", caddr
);
548 return (wsp
->walk_callback(caddr
, &cc
, wsp
->walk_cbdata
));
552 umem_slab_walk_init(mdb_walk_state_t
*wsp
)
554 uintptr_t caddr
= wsp
->walk_addr
;
558 mdb_warn("umem_slab doesn't support global walks\n");
562 if (mdb_vread(&c
, sizeof (c
), caddr
) == -1) {
563 mdb_warn("couldn't read umem_cache at %p", caddr
);
568 (void *)(caddr
+ offsetof(umem_cache_t
, cache_nullslab
));
569 wsp
->walk_addr
= (uintptr_t)c
.cache_nullslab
.slab_next
;
575 umem_slab_walk_partial_init(mdb_walk_state_t
*wsp
)
577 uintptr_t caddr
= wsp
->walk_addr
;
581 mdb_warn("umem_slab_partial doesn't support global walks\n");
585 if (mdb_vread(&c
, sizeof (c
), caddr
) == -1) {
586 mdb_warn("couldn't read umem_cache at %p", caddr
);
591 (void *)(caddr
+ offsetof(umem_cache_t
, cache_nullslab
));
592 wsp
->walk_addr
= (uintptr_t)c
.cache_freelist
;
595 * Some consumers (umem_walk_step(), in particular) require at
596 * least one callback if there are any buffers in the cache. So
597 * if there are *no* partial slabs, report the last full slab, if
600 * Yes, this is ugly, but it's cleaner than the other possibilities.
602 if ((uintptr_t)wsp
->walk_data
== wsp
->walk_addr
)
603 wsp
->walk_addr
= (uintptr_t)c
.cache_nullslab
.slab_prev
;
609 umem_slab_walk_step(mdb_walk_state_t
*wsp
)
612 uintptr_t addr
= wsp
->walk_addr
;
613 uintptr_t saddr
= (uintptr_t)wsp
->walk_data
;
614 uintptr_t caddr
= saddr
- offsetof(umem_cache_t
, cache_nullslab
);
619 if (mdb_vread(&s
, sizeof (s
), addr
) == -1) {
620 mdb_warn("failed to read slab at %p", wsp
->walk_addr
);
624 if ((uintptr_t)s
.slab_cache
!= caddr
) {
625 mdb_warn("slab %p isn't in cache %p (in cache %p)\n",
626 addr
, caddr
, s
.slab_cache
);
630 wsp
->walk_addr
= (uintptr_t)s
.slab_next
;
632 return (wsp
->walk_callback(addr
, &s
, wsp
->walk_cbdata
));
636 umem_cache(uintptr_t addr
, uint_t flags
, int ac
, const mdb_arg_t
*argv
)
640 if (!(flags
& DCMD_ADDRSPEC
)) {
641 if (mdb_walk_dcmd("umem_cache", "umem_cache", ac
, argv
) == -1) {
642 mdb_warn("can't walk umem_cache");
648 if (DCMD_HDRSPEC(flags
))
649 mdb_printf("%-?s %-25s %4s %8s %8s %8s\n", "ADDR", "NAME",
650 "FLAG", "CFLAG", "BUFSIZE", "BUFTOTL");
652 if (mdb_vread(&c
, sizeof (c
), addr
) == -1) {
653 mdb_warn("couldn't read umem_cache at %p", addr
);
657 mdb_printf("%0?p %-25s %04x %08x %8ld %8lld\n", addr
, c
.cache_name
,
658 c
.cache_flags
, c
.cache_cflags
, c
.cache_bufsize
, c
.cache_buftotal
);
664 addrcmp(const void *lhs
, const void *rhs
)
666 uintptr_t p1
= *((uintptr_t *)lhs
);
667 uintptr_t p2
= *((uintptr_t *)rhs
);
677 bufctlcmp(const umem_bufctl_audit_t
**lhs
, const umem_bufctl_audit_t
**rhs
)
679 const umem_bufctl_audit_t
*bcp1
= *lhs
;
680 const umem_bufctl_audit_t
*bcp2
= *rhs
;
682 if (bcp1
->bc_timestamp
> bcp2
->bc_timestamp
)
685 if (bcp1
->bc_timestamp
< bcp2
->bc_timestamp
)
691 typedef struct umem_hash_walk
{
692 uintptr_t *umhw_table
;
695 umem_bufctl_t umhw_cur
;
699 umem_hash_walk_init(mdb_walk_state_t
*wsp
)
701 umem_hash_walk_t
*umhw
;
704 uintptr_t haddr
, addr
= wsp
->walk_addr
;
709 mdb_warn("umem_hash doesn't support global walks\n");
713 if (mdb_vread(&c
, sizeof (c
), addr
) == -1) {
714 mdb_warn("couldn't read cache at addr %p", addr
);
718 if (!(c
.cache_flags
& UMF_HASH
)) {
719 mdb_warn("cache %p doesn't have a hash table\n", addr
);
720 return (WALK_DONE
); /* nothing to do */
723 umhw
= mdb_zalloc(sizeof (umem_hash_walk_t
), UM_SLEEP
);
724 umhw
->umhw_cur
.bc_next
= NULL
;
727 umhw
->umhw_nelems
= nelems
= c
.cache_hash_mask
+ 1;
728 hsize
= nelems
* sizeof (uintptr_t);
729 haddr
= (uintptr_t)c
.cache_hash_table
;
731 umhw
->umhw_table
= hash
= mdb_alloc(hsize
, UM_SLEEP
);
732 if (mdb_vread(hash
, hsize
, haddr
) == -1) {
733 mdb_warn("failed to read hash table at %p", haddr
);
734 mdb_free(hash
, hsize
);
735 mdb_free(umhw
, sizeof (umem_hash_walk_t
));
739 wsp
->walk_data
= umhw
;
745 umem_hash_walk_step(mdb_walk_state_t
*wsp
)
747 umem_hash_walk_t
*umhw
= wsp
->walk_data
;
748 uintptr_t addr
= NULL
;
750 if ((addr
= (uintptr_t)umhw
->umhw_cur
.bc_next
) == NULL
) {
751 while (umhw
->umhw_pos
< umhw
->umhw_nelems
) {
752 if ((addr
= umhw
->umhw_table
[umhw
->umhw_pos
++]) != NULL
)
759 if (mdb_vread(&umhw
->umhw_cur
, sizeof (umem_bufctl_t
), addr
) == -1) {
760 mdb_warn("couldn't read umem_bufctl_t at addr %p", addr
);
764 return (wsp
->walk_callback(addr
, &umhw
->umhw_cur
, wsp
->walk_cbdata
));
768 umem_hash_walk_fini(mdb_walk_state_t
*wsp
)
770 umem_hash_walk_t
*umhw
= wsp
->walk_data
;
775 mdb_free(umhw
->umhw_table
, umhw
->umhw_nelems
* sizeof (uintptr_t));
776 mdb_free(umhw
, sizeof (umem_hash_walk_t
));
780 * Find the address of the bufctl structure for the address 'buf' in cache
781 * 'cp', which is at address caddr, and place it in *out.
784 umem_hash_lookup(umem_cache_t
*cp
, uintptr_t caddr
, void *buf
, uintptr_t *out
)
786 uintptr_t bucket
= (uintptr_t)UMEM_HASH(cp
, buf
);
790 if (mdb_vread(&bcp
, sizeof (umem_bufctl_t
*), bucket
) == -1) {
791 mdb_warn("unable to read hash bucket for %p in cache %p",
796 while (bcp
!= NULL
) {
797 if (mdb_vread(&bc
, sizeof (umem_bufctl_t
),
798 (uintptr_t)bcp
) == -1) {
799 mdb_warn("unable to read bufctl at %p", bcp
);
802 if (bc
.bc_addr
== buf
) {
803 *out
= (uintptr_t)bcp
;
809 mdb_warn("unable to find bufctl for %p in cache %p\n", buf
, caddr
);
814 umem_get_magsize(const umem_cache_t
*cp
)
816 uintptr_t addr
= (uintptr_t)cp
->cache_magtype
;
822 * if cpu 0 has a non-zero magsize, it must be correct. caches
823 * with UMF_NOMAGAZINE have disabled their magazine layers, so
824 * it is okay to return 0 for them.
826 if ((res
= cp
->cache_cpu
[0].cc_magsize
) != 0 ||
827 (cp
->cache_flags
& UMF_NOMAGAZINE
))
830 if (umem_lookup_by_name("umem_magtype", &mt_sym
) == -1) {
831 mdb_warn("unable to read 'umem_magtype'");
832 } else if (addr
< mt_sym
.st_value
||
833 addr
+ sizeof (mt
) - 1 > mt_sym
.st_value
+ mt_sym
.st_size
- 1 ||
834 ((addr
- mt_sym
.st_value
) % sizeof (mt
)) != 0) {
835 mdb_warn("cache '%s' has invalid magtype pointer (%p)\n",
836 cp
->cache_name
, addr
);
839 if (mdb_vread(&mt
, sizeof (mt
), addr
) == -1) {
840 mdb_warn("unable to read magtype at %a", addr
);
843 return (mt
.mt_magsize
);
848 umem_estimate_slab(uintptr_t addr
, const umem_slab_t
*sp
, size_t *est
)
850 *est
-= (sp
->slab_chunks
- sp
->slab_refcnt
);
856 * Returns an upper bound on the number of allocated buffers in a given
860 umem_estimate_allocated(uintptr_t addr
, const umem_cache_t
*cp
)
865 cache_est
= cp
->cache_buftotal
;
867 (void) mdb_pwalk("umem_slab_partial",
868 (mdb_walk_cb_t
)umem_estimate_slab
, &cache_est
, addr
);
870 if ((magsize
= umem_get_magsize(cp
)) != 0) {
871 size_t mag_est
= cp
->cache_full
.ml_total
* magsize
;
873 if (cache_est
>= mag_est
) {
874 cache_est
-= mag_est
;
876 mdb_warn("cache %p's magazine layer holds more buffers "
877 "than the slab layer.\n", addr
);
883 #define READMAG_ROUNDS(rounds) { \
884 if (mdb_vread(mp, magbsize, (uintptr_t)ump) == -1) { \
885 mdb_warn("couldn't read magazine at %p", ump); \
888 for (i = 0; i < rounds; i++) { \
889 maglist[magcnt++] = mp->mag_round[i]; \
890 if (magcnt == magmax) { \
891 mdb_warn("%d magazines exceeds fudge factor\n", \
899 umem_read_magazines(umem_cache_t
*cp
, uintptr_t addr
,
900 void ***maglistp
, size_t *magcntp
, size_t *magmaxp
)
902 umem_magazine_t
*ump
, *mp
;
903 void **maglist
= NULL
;
905 size_t magsize
, magmax
, magbsize
;
909 * Read the magtype out of the cache, after verifying the pointer's
912 magsize
= umem_get_magsize(cp
);
921 * There are several places where we need to go buffer hunting:
922 * the per-CPU loaded magazine, the per-CPU spare full magazine,
923 * and the full magazine list in the depot.
925 * For an upper bound on the number of buffers in the magazine
926 * layer, we have the number of magazines on the cache_full
927 * list plus at most two magazines per CPU (the loaded and the
928 * spare). Toss in 100 magazines as a fudge factor in case this
929 * is live (the number "100" comes from the same fudge factor in
932 magmax
= (cp
->cache_full
.ml_total
+ 2 * umem_max_ncpus
+ 100) * magsize
;
933 magbsize
= offsetof(umem_magazine_t
, mag_round
[magsize
]);
935 if (magbsize
>= PAGESIZE
/ 2) {
936 mdb_warn("magazine size for cache %p unreasonable (%x)\n",
941 maglist
= mdb_alloc(magmax
* sizeof (void *), UM_SLEEP
);
942 mp
= mdb_alloc(magbsize
, UM_SLEEP
);
943 if (mp
== NULL
|| maglist
== NULL
)
947 * First up: the magazines in the depot (i.e. on the cache_full list).
949 for (ump
= cp
->cache_full
.ml_list
; ump
!= NULL
; ) {
950 READMAG_ROUNDS(magsize
);
953 if (ump
== cp
->cache_full
.ml_list
)
954 break; /* cache_full list loop detected */
957 dprintf(("cache_full list done\n"));
960 * Now whip through the CPUs, snagging the loaded magazines
963 for (cpu
= 0; cpu
< umem_max_ncpus
; cpu
++) {
964 umem_cpu_cache_t
*ccp
= &cp
->cache_cpu
[cpu
];
966 dprintf(("reading cpu cache %p\n",
967 (uintptr_t)ccp
- (uintptr_t)cp
+ addr
));
969 if (ccp
->cc_rounds
> 0 &&
970 (ump
= ccp
->cc_loaded
) != NULL
) {
971 dprintf(("reading %d loaded rounds\n", ccp
->cc_rounds
));
972 READMAG_ROUNDS(ccp
->cc_rounds
);
975 if (ccp
->cc_prounds
> 0 &&
976 (ump
= ccp
->cc_ploaded
) != NULL
) {
977 dprintf(("reading %d previously loaded rounds\n",
979 READMAG_ROUNDS(ccp
->cc_prounds
);
983 dprintf(("magazine layer: %d buffers\n", magcnt
));
985 mdb_free(mp
, magbsize
);
995 mdb_free(mp
, magbsize
);
997 mdb_free(maglist
, magmax
* sizeof (void *));
1002 typedef struct umem_read_ptc_walk
{
1006 } umem_read_ptc_walk_t
;
1010 umem_read_ptc_walk_buf(uintptr_t addr
,
1011 const void *ignored
, umem_read_ptc_walk_t
*urpw
)
1013 if (urpw
->urpw_cnt
== urpw
->urpw_max
) {
1014 size_t nmax
= urpw
->urpw_max
? (urpw
->urpw_max
<< 1) : 1;
1015 void **new = mdb_zalloc(nmax
* sizeof (void *), UM_SLEEP
);
1018 size_t osize
= urpw
->urpw_max
* sizeof (void *);
1019 bcopy(urpw
->urpw_buf
, new, osize
);
1020 mdb_free(urpw
->urpw_buf
, osize
);
1023 urpw
->urpw_buf
= new;
1024 urpw
->urpw_max
= nmax
;
1027 urpw
->urpw_buf
[urpw
->urpw_cnt
++] = (void *)addr
;
1033 umem_read_ptc(umem_cache_t
*cp
,
1034 void ***buflistp
, size_t *bufcntp
, size_t *bufmaxp
)
1036 umem_read_ptc_walk_t urpw
;
1040 if (!(cp
->cache_flags
& UMF_PTC
))
1043 (void) mdb_snprintf(walk
, sizeof (walk
), "umem_ptc_%d",
1046 urpw
.urpw_buf
= *buflistp
;
1047 urpw
.urpw_cnt
= *bufcntp
;
1048 urpw
.urpw_max
= *bufmaxp
;
1050 if ((rval
= mdb_walk(walk
,
1051 (mdb_walk_cb_t
)umem_read_ptc_walk_buf
, &urpw
)) == -1) {
1052 mdb_warn("couldn't walk %s", walk
);
1055 *buflistp
= urpw
.urpw_buf
;
1056 *bufcntp
= urpw
.urpw_cnt
;
1057 *bufmaxp
= urpw
.urpw_max
;
1063 umem_walk_callback(mdb_walk_state_t
*wsp
, uintptr_t buf
)
1065 return (wsp
->walk_callback(buf
, NULL
, wsp
->walk_cbdata
));
1069 bufctl_walk_callback(umem_cache_t
*cp
, mdb_walk_state_t
*wsp
, uintptr_t buf
)
1071 umem_bufctl_audit_t
*b
;
1072 UMEM_LOCAL_BUFCTL_AUDIT(&b
);
1075 * if UMF_AUDIT is not set, we know that we're looking at a
1078 if (!(cp
->cache_flags
& UMF_AUDIT
) ||
1079 mdb_vread(b
, UMEM_BUFCTL_AUDIT_SIZE
, buf
) == -1) {
1080 (void) memset(b
, 0, UMEM_BUFCTL_AUDIT_SIZE
);
1081 if (mdb_vread(b
, sizeof (umem_bufctl_t
), buf
) == -1) {
1082 mdb_warn("unable to read bufctl at %p", buf
);
1087 return (wsp
->walk_callback(buf
, b
, wsp
->walk_cbdata
));
1090 typedef struct umem_walk
{
1093 uintptr_t umw_addr
; /* cache address */
1094 umem_cache_t
*umw_cp
;
1108 char *umw_valid
; /* to keep track of freed buffers */
1109 char *umw_ubase
; /* buffer for slab data */
1113 umem_walk_init_common(mdb_walk_state_t
*wsp
, int type
)
1120 size_t magmax
, magcnt
;
1121 void **maglist
= NULL
;
1122 uint_t chunksize
, slabsize
;
1123 int status
= WALK_ERR
;
1124 uintptr_t addr
= wsp
->walk_addr
;
1125 const char *layered
;
1130 mdb_warn("umem walk doesn't support global walks\n");
1134 dprintf(("walking %p\n", addr
));
1137 * The number of "cpus" determines how large the cache is.
1139 csize
= UMEM_CACHE_SIZE(umem_max_ncpus
);
1140 cp
= mdb_alloc(csize
, UM_SLEEP
);
1142 if (mdb_vread(cp
, csize
, addr
) == -1) {
1143 mdb_warn("couldn't read cache at addr %p", addr
);
1148 * It's easy for someone to hand us an invalid cache address.
1149 * Unfortunately, it is hard for this walker to survive an
1150 * invalid cache cleanly. So we make sure that:
1152 * 1. the vmem arena for the cache is readable,
1153 * 2. the vmem arena's quantum is a power of 2,
1154 * 3. our slabsize is a multiple of the quantum, and
1155 * 4. our chunksize is >0 and less than our slabsize.
1157 if (mdb_vread(&vm_quantum
, sizeof (vm_quantum
),
1158 (uintptr_t)&cp
->cache_arena
->vm_quantum
) == -1 ||
1160 (vm_quantum
& (vm_quantum
- 1)) != 0 ||
1161 cp
->cache_slabsize
< vm_quantum
||
1162 P2PHASE(cp
->cache_slabsize
, vm_quantum
) != 0 ||
1163 cp
->cache_chunksize
== 0 ||
1164 cp
->cache_chunksize
> cp
->cache_slabsize
) {
1165 mdb_warn("%p is not a valid umem_cache_t\n", addr
);
1169 dprintf(("buf total is %d\n", cp
->cache_buftotal
));
1171 if (cp
->cache_buftotal
== 0) {
1172 mdb_free(cp
, csize
);
1177 * If they ask for bufctls, but it's a small-slab cache,
1178 * there is nothing to report.
1180 if ((type
& UM_BUFCTL
) && !(cp
->cache_flags
& UMF_HASH
)) {
1181 dprintf(("bufctl requested, not UMF_HASH (flags: %p)\n",
1183 mdb_free(cp
, csize
);
1188 * Read in the contents of the magazine layer
1190 if (umem_read_magazines(cp
, addr
, &maglist
, &magcnt
, &magmax
) != 0)
1194 * Read in the contents of the per-thread caches, if any
1196 if (umem_read_ptc(cp
, &maglist
, &magcnt
, &magmax
) != 0)
1200 * We have all of the buffers from the magazines and from the
1201 * per-thread cache (if any); if we are walking allocated buffers,
1202 * sort them so we can bsearch them later.
1204 if (type
& UM_ALLOCATED
)
1205 qsort(maglist
, magcnt
, sizeof (void *), addrcmp
);
1207 wsp
->walk_data
= umw
= mdb_zalloc(sizeof (umem_walk_t
), UM_SLEEP
);
1209 umw
->umw_type
= type
;
1210 umw
->umw_addr
= addr
;
1212 umw
->umw_csize
= csize
;
1213 umw
->umw_maglist
= maglist
;
1214 umw
->umw_max
= magmax
;
1215 umw
->umw_count
= magcnt
;
1219 * When walking allocated buffers in a UMF_HASH cache, we walk the
1220 * hash table instead of the slab layer.
1222 if ((cp
->cache_flags
& UMF_HASH
) && (type
& UM_ALLOCATED
)) {
1223 layered
= "umem_hash";
1225 umw
->umw_type
|= UM_HASH
;
1228 * If we are walking freed buffers, we only need the
1229 * magazine layer plus the partially allocated slabs.
1230 * To walk allocated buffers, we need all of the slabs.
1232 if (type
& UM_ALLOCATED
)
1233 layered
= "umem_slab";
1235 layered
= "umem_slab_partial";
1238 * for small-slab caches, we read in the entire slab. For
1239 * freed buffers, we can just walk the freelist. For
1240 * allocated buffers, we use a 'valid' array to track
1241 * the freed buffers.
1243 if (!(cp
->cache_flags
& UMF_HASH
)) {
1244 chunksize
= cp
->cache_chunksize
;
1245 slabsize
= cp
->cache_slabsize
;
1247 umw
->umw_ubase
= mdb_alloc(slabsize
+
1248 sizeof (umem_bufctl_t
), UM_SLEEP
);
1250 if (type
& UM_ALLOCATED
)
1252 mdb_alloc(slabsize
/ chunksize
, UM_SLEEP
);
1258 if (mdb_layered_walk(layered
, wsp
) == -1) {
1259 mdb_warn("unable to start layered '%s' walk", layered
);
1264 if (status
== WALK_ERR
) {
1266 mdb_free(umw
->umw_valid
, slabsize
/ chunksize
);
1269 mdb_free(umw
->umw_ubase
, slabsize
+
1270 sizeof (umem_bufctl_t
));
1272 if (umw
->umw_maglist
)
1273 mdb_free(umw
->umw_maglist
, umw
->umw_max
*
1274 sizeof (uintptr_t));
1276 mdb_free(umw
, sizeof (umem_walk_t
));
1277 wsp
->walk_data
= NULL
;
1281 if (status
== WALK_ERR
)
1282 mdb_free(cp
, csize
);
1288 umem_walk_step(mdb_walk_state_t
*wsp
)
1290 umem_walk_t
*umw
= wsp
->walk_data
;
1291 int type
= umw
->umw_type
;
1292 umem_cache_t
*cp
= umw
->umw_cp
;
1294 void **maglist
= umw
->umw_maglist
;
1295 int magcnt
= umw
->umw_count
;
1297 uintptr_t chunksize
, slabsize
;
1299 const umem_slab_t
*sp
;
1300 const umem_bufctl_t
*bcp
;
1308 char *valid
, *ubase
;
1311 * first, handle the 'umem_hash' layered walk case
1313 if (type
& UM_HASH
) {
1315 * We have a buffer which has been allocated out of the
1316 * global layer. We need to make sure that it's not
1317 * actually sitting in a magazine before we report it as
1318 * an allocated buffer.
1320 buf
= ((const umem_bufctl_t
*)wsp
->walk_layer
)->bc_addr
;
1323 bsearch(&buf
, maglist
, magcnt
, sizeof (void *),
1327 if (type
& UM_BUFCTL
)
1328 return (bufctl_walk_callback(cp
, wsp
, wsp
->walk_addr
));
1330 return (umem_walk_callback(wsp
, (uintptr_t)buf
));
1335 addr
= umw
->umw_addr
;
1338 * If we're walking freed buffers, report everything in the
1339 * magazine layer before processing the first slab.
1341 if ((type
& UM_FREE
) && magcnt
!= 0) {
1342 umw
->umw_count
= 0; /* only do this once */
1343 for (i
= 0; i
< magcnt
; i
++) {
1346 if (type
& UM_BUFCTL
) {
1349 if (cp
->cache_flags
& UMF_BUFTAG
) {
1353 /* LINTED - alignment */
1354 btp
= UMEM_BUFTAG(cp
, buf
);
1355 if (mdb_vread(&tag
, sizeof (tag
),
1356 (uintptr_t)btp
) == -1) {
1357 mdb_warn("reading buftag for "
1358 "%p at %p", buf
, btp
);
1361 out
= (uintptr_t)tag
.bt_bufctl
;
1363 if (umem_hash_lookup(cp
, addr
, buf
,
1367 ret
= bufctl_walk_callback(cp
, wsp
, out
);
1369 ret
= umem_walk_callback(wsp
, (uintptr_t)buf
);
1372 if (ret
!= WALK_NEXT
)
1378 * Handle the buffers in the current slab
1380 chunksize
= cp
->cache_chunksize
;
1381 slabsize
= cp
->cache_slabsize
;
1383 sp
= wsp
->walk_layer
;
1384 chunks
= sp
->slab_chunks
;
1385 kbase
= sp
->slab_base
;
1387 dprintf(("kbase is %p\n", kbase
));
1389 if (!(cp
->cache_flags
& UMF_HASH
)) {
1390 valid
= umw
->umw_valid
;
1391 ubase
= umw
->umw_ubase
;
1393 if (mdb_vread(ubase
, chunks
* chunksize
,
1394 (uintptr_t)kbase
) == -1) {
1395 mdb_warn("failed to read slab contents at %p", kbase
);
1400 * Set up the valid map as fully allocated -- we'll punch
1403 if (type
& UM_ALLOCATED
)
1404 (void) memset(valid
, 1, chunks
);
1411 * walk the slab's freelist
1413 bcp
= sp
->slab_head
;
1415 dprintf(("refcnt is %d; chunks is %d\n", sp
->slab_refcnt
, chunks
));
1418 * since we could be in the middle of allocating a buffer,
1419 * our refcnt could be one higher than it aught. So we
1420 * check one further on the freelist than the count allows.
1422 for (i
= sp
->slab_refcnt
; i
<= chunks
; i
++) {
1425 dprintf(("bcp is %p\n", bcp
));
1431 "slab %p in cache %p freelist too short by %d\n",
1432 sp
, addr
, chunks
- i
);
1436 if (cp
->cache_flags
& UMF_HASH
) {
1437 if (mdb_vread(&bc
, sizeof (bc
), (uintptr_t)bcp
) == -1) {
1438 mdb_warn("failed to read bufctl ptr at %p",
1445 * Otherwise the buffer is (or should be) in the slab
1446 * that we've read in; determine its offset in the
1447 * slab, validate that it's not corrupt, and add to
1448 * our base address to find the umem_bufctl_t. (Note
1449 * that we don't need to add the size of the bufctl
1450 * to our offset calculation because of the slop that's
1451 * allocated for the buffer at ubase.)
1453 uintptr_t offs
= (uintptr_t)bcp
- (uintptr_t)kbase
;
1455 if (offs
> chunks
* chunksize
) {
1456 mdb_warn("found corrupt bufctl ptr %p"
1457 " in slab %p in cache %p\n", bcp
,
1458 wsp
->walk_addr
, addr
);
1462 bc
= *((umem_bufctl_t
*)((uintptr_t)ubase
+ offs
));
1463 buf
= UMEM_BUF(cp
, bcp
);
1466 ndx
= ((uintptr_t)buf
- (uintptr_t)kbase
) / chunksize
;
1468 if (ndx
> slabsize
/ cp
->cache_bufsize
) {
1470 * This is very wrong; we have managed to find
1471 * a buffer in the slab which shouldn't
1472 * actually be here. Emit a warning, and
1475 mdb_warn("buf %p is out of range for "
1476 "slab %p, cache %p\n", buf
, sp
, addr
);
1477 } else if (type
& UM_ALLOCATED
) {
1479 * we have found a buffer on the slab's freelist;
1485 * Report this freed buffer
1487 if (type
& UM_BUFCTL
) {
1488 ret
= bufctl_walk_callback(cp
, wsp
,
1491 ret
= umem_walk_callback(wsp
, (uintptr_t)buf
);
1493 if (ret
!= WALK_NEXT
)
1501 dprintf(("slab %p in cache %p freelist too long (%p)\n",
1506 * If we are walking freed buffers, the loop above handled reporting
1512 if (type
& UM_BUFCTL
) {
1513 mdb_warn("impossible situation: small-slab UM_BUFCTL walk for "
1514 "cache %p\n", addr
);
1519 * Report allocated buffers, skipping buffers in the magazine layer.
1520 * We only get this far for small-slab caches.
1522 for (i
= 0; ret
== WALK_NEXT
&& i
< chunks
; i
++) {
1523 buf
= (char *)kbase
+ i
* chunksize
;
1526 continue; /* on slab freelist */
1529 bsearch(&buf
, maglist
, magcnt
, sizeof (void *),
1531 continue; /* in magazine layer */
1533 ret
= umem_walk_callback(wsp
, (uintptr_t)buf
);
1539 umem_walk_fini(mdb_walk_state_t
*wsp
)
1541 umem_walk_t
*umw
= wsp
->walk_data
;
1542 uintptr_t chunksize
;
1548 if (umw
->umw_maglist
!= NULL
)
1549 mdb_free(umw
->umw_maglist
, umw
->umw_max
* sizeof (void *));
1551 chunksize
= umw
->umw_cp
->cache_chunksize
;
1552 slabsize
= umw
->umw_cp
->cache_slabsize
;
1554 if (umw
->umw_valid
!= NULL
)
1555 mdb_free(umw
->umw_valid
, slabsize
/ chunksize
);
1556 if (umw
->umw_ubase
!= NULL
)
1557 mdb_free(umw
->umw_ubase
, slabsize
+ sizeof (umem_bufctl_t
));
1559 mdb_free(umw
->umw_cp
, umw
->umw_csize
);
1560 mdb_free(umw
, sizeof (umem_walk_t
));
1565 umem_walk_all(uintptr_t addr
, const umem_cache_t
*c
, mdb_walk_state_t
*wsp
)
1568 * Buffers allocated from NOTOUCH caches can also show up as freed
1569 * memory in other caches. This can be a little confusing, so we
1570 * don't walk NOTOUCH caches when walking all caches (thereby assuring
1571 * that "::walk umem" and "::walk freemem" yield disjoint output).
1573 if (c
->cache_cflags
& UMC_NOTOUCH
)
1576 if (mdb_pwalk(wsp
->walk_data
, wsp
->walk_callback
,
1577 wsp
->walk_cbdata
, addr
) == -1)
1583 #define UMEM_WALK_ALL(name, wsp) { \
1584 wsp->walk_data = (name); \
1585 if (mdb_walk("umem_cache", (mdb_walk_cb_t)umem_walk_all, wsp) == -1) \
1586 return (WALK_ERR); \
1587 return (WALK_DONE); \
1591 umem_walk_init(mdb_walk_state_t
*wsp
)
1593 if (wsp
->walk_arg
!= NULL
)
1594 wsp
->walk_addr
= (uintptr_t)wsp
->walk_arg
;
1596 if (wsp
->walk_addr
== NULL
)
1597 UMEM_WALK_ALL("umem", wsp
);
1598 return (umem_walk_init_common(wsp
, UM_ALLOCATED
));
1602 bufctl_walk_init(mdb_walk_state_t
*wsp
)
1604 if (wsp
->walk_addr
== NULL
)
1605 UMEM_WALK_ALL("bufctl", wsp
);
1606 return (umem_walk_init_common(wsp
, UM_ALLOCATED
| UM_BUFCTL
));
1610 freemem_walk_init(mdb_walk_state_t
*wsp
)
1612 if (wsp
->walk_addr
== NULL
)
1613 UMEM_WALK_ALL("freemem", wsp
);
1614 return (umem_walk_init_common(wsp
, UM_FREE
));
1618 freectl_walk_init(mdb_walk_state_t
*wsp
)
1620 if (wsp
->walk_addr
== NULL
)
1621 UMEM_WALK_ALL("freectl", wsp
);
1622 return (umem_walk_init_common(wsp
, UM_FREE
| UM_BUFCTL
));
1625 typedef struct bufctl_history_walk
{
1627 umem_cache_t
*bhw_cache
;
1628 umem_slab_t
*bhw_slab
;
1629 hrtime_t bhw_timestamp
;
1630 } bufctl_history_walk_t
;
1633 bufctl_history_walk_init(mdb_walk_state_t
*wsp
)
1635 bufctl_history_walk_t
*bhw
;
1636 umem_bufctl_audit_t bc
;
1637 umem_bufctl_audit_t bcn
;
1639 if (wsp
->walk_addr
== NULL
) {
1640 mdb_warn("bufctl_history walk doesn't support global walks\n");
1644 if (mdb_vread(&bc
, sizeof (bc
), wsp
->walk_addr
) == -1) {
1645 mdb_warn("unable to read bufctl at %p", wsp
->walk_addr
);
1649 bhw
= mdb_zalloc(sizeof (*bhw
), UM_SLEEP
);
1650 bhw
->bhw_timestamp
= 0;
1651 bhw
->bhw_cache
= bc
.bc_cache
;
1652 bhw
->bhw_slab
= bc
.bc_slab
;
1655 * sometimes the first log entry matches the base bufctl; in that
1656 * case, skip the base bufctl.
1658 if (bc
.bc_lastlog
!= NULL
&&
1659 mdb_vread(&bcn
, sizeof (bcn
), (uintptr_t)bc
.bc_lastlog
) != -1 &&
1660 bc
.bc_addr
== bcn
.bc_addr
&&
1661 bc
.bc_cache
== bcn
.bc_cache
&&
1662 bc
.bc_slab
== bcn
.bc_slab
&&
1663 bc
.bc_timestamp
== bcn
.bc_timestamp
&&
1664 bc
.bc_thread
== bcn
.bc_thread
)
1665 bhw
->bhw_next
= bc
.bc_lastlog
;
1667 bhw
->bhw_next
= (void *)wsp
->walk_addr
;
1669 wsp
->walk_addr
= (uintptr_t)bc
.bc_addr
;
1670 wsp
->walk_data
= bhw
;
1676 bufctl_history_walk_step(mdb_walk_state_t
*wsp
)
1678 bufctl_history_walk_t
*bhw
= wsp
->walk_data
;
1679 uintptr_t addr
= (uintptr_t)bhw
->bhw_next
;
1680 uintptr_t baseaddr
= wsp
->walk_addr
;
1681 umem_bufctl_audit_t
*b
;
1682 UMEM_LOCAL_BUFCTL_AUDIT(&b
);
1687 if (mdb_vread(b
, UMEM_BUFCTL_AUDIT_SIZE
, addr
) == -1) {
1688 mdb_warn("unable to read bufctl at %p", bhw
->bhw_next
);
1693 * The bufctl is only valid if the address, cache, and slab are
1694 * correct. We also check that the timestamp is decreasing, to
1695 * prevent infinite loops.
1697 if ((uintptr_t)b
->bc_addr
!= baseaddr
||
1698 b
->bc_cache
!= bhw
->bhw_cache
||
1699 b
->bc_slab
!= bhw
->bhw_slab
||
1700 (bhw
->bhw_timestamp
!= 0 && b
->bc_timestamp
>= bhw
->bhw_timestamp
))
1703 bhw
->bhw_next
= b
->bc_lastlog
;
1704 bhw
->bhw_timestamp
= b
->bc_timestamp
;
1706 return (wsp
->walk_callback(addr
, b
, wsp
->walk_cbdata
));
1710 bufctl_history_walk_fini(mdb_walk_state_t
*wsp
)
1712 bufctl_history_walk_t
*bhw
= wsp
->walk_data
;
1714 mdb_free(bhw
, sizeof (*bhw
));
1717 typedef struct umem_log_walk
{
1718 umem_bufctl_audit_t
*ulw_base
;
1719 umem_bufctl_audit_t
**ulw_sorted
;
1720 umem_log_header_t ulw_lh
;
1727 umem_log_walk_init(mdb_walk_state_t
*wsp
)
1729 uintptr_t lp
= wsp
->walk_addr
;
1730 umem_log_walk_t
*ulw
;
1731 umem_log_header_t
*lhp
;
1732 int maxndx
, i
, j
, k
;
1735 * By default (global walk), walk the umem_transaction_log. Otherwise
1736 * read the log whose umem_log_header_t is stored at walk_addr.
1738 if (lp
== NULL
&& umem_readvar(&lp
, "umem_transaction_log") == -1) {
1739 mdb_warn("failed to read 'umem_transaction_log'");
1744 mdb_warn("log is disabled\n");
1748 ulw
= mdb_zalloc(sizeof (umem_log_walk_t
), UM_SLEEP
);
1751 if (mdb_vread(lhp
, sizeof (umem_log_header_t
), lp
) == -1) {
1752 mdb_warn("failed to read log header at %p", lp
);
1753 mdb_free(ulw
, sizeof (umem_log_walk_t
));
1757 ulw
->ulw_size
= lhp
->lh_chunksize
* lhp
->lh_nchunks
;
1758 ulw
->ulw_base
= mdb_alloc(ulw
->ulw_size
, UM_SLEEP
);
1759 maxndx
= lhp
->lh_chunksize
/ UMEM_BUFCTL_AUDIT_SIZE
- 1;
1761 if (mdb_vread(ulw
->ulw_base
, ulw
->ulw_size
,
1762 (uintptr_t)lhp
->lh_base
) == -1) {
1763 mdb_warn("failed to read log at base %p", lhp
->lh_base
);
1764 mdb_free(ulw
->ulw_base
, ulw
->ulw_size
);
1765 mdb_free(ulw
, sizeof (umem_log_walk_t
));
1769 ulw
->ulw_sorted
= mdb_alloc(maxndx
* lhp
->lh_nchunks
*
1770 sizeof (umem_bufctl_audit_t
*), UM_SLEEP
);
1772 for (i
= 0, k
= 0; i
< lhp
->lh_nchunks
; i
++) {
1773 caddr_t chunk
= (caddr_t
)
1774 ((uintptr_t)ulw
->ulw_base
+ i
* lhp
->lh_chunksize
);
1776 for (j
= 0; j
< maxndx
; j
++) {
1778 ulw
->ulw_sorted
[k
++] = (umem_bufctl_audit_t
*)chunk
;
1779 chunk
+= UMEM_BUFCTL_AUDIT_SIZE
;
1783 qsort(ulw
->ulw_sorted
, k
, sizeof (umem_bufctl_audit_t
*),
1784 (int(*)(const void *, const void *))bufctlcmp
);
1786 ulw
->ulw_maxndx
= k
;
1787 wsp
->walk_data
= ulw
;
1793 umem_log_walk_step(mdb_walk_state_t
*wsp
)
1795 umem_log_walk_t
*ulw
= wsp
->walk_data
;
1796 umem_bufctl_audit_t
*bcp
;
1798 if (ulw
->ulw_ndx
== ulw
->ulw_maxndx
)
1801 bcp
= ulw
->ulw_sorted
[ulw
->ulw_ndx
++];
1803 return (wsp
->walk_callback((uintptr_t)bcp
- (uintptr_t)ulw
->ulw_base
+
1804 (uintptr_t)ulw
->ulw_lh
.lh_base
, bcp
, wsp
->walk_cbdata
));
1808 umem_log_walk_fini(mdb_walk_state_t
*wsp
)
1810 umem_log_walk_t
*ulw
= wsp
->walk_data
;
1812 mdb_free(ulw
->ulw_base
, ulw
->ulw_size
);
1813 mdb_free(ulw
->ulw_sorted
, ulw
->ulw_maxndx
*
1814 sizeof (umem_bufctl_audit_t
*));
1815 mdb_free(ulw
, sizeof (umem_log_walk_t
));
1818 typedef struct allocdby_bufctl
{
1821 } allocdby_bufctl_t
;
1823 typedef struct allocdby_walk
{
1824 const char *abw_walk
;
1825 uintptr_t abw_thread
;
1828 allocdby_bufctl_t
*abw_buf
;
1833 allocdby_walk_bufctl(uintptr_t addr
, const umem_bufctl_audit_t
*bcp
,
1834 allocdby_walk_t
*abw
)
1836 if ((uintptr_t)bcp
->bc_thread
!= abw
->abw_thread
)
1839 if (abw
->abw_nbufs
== abw
->abw_size
) {
1840 allocdby_bufctl_t
*buf
;
1841 size_t oldsize
= sizeof (allocdby_bufctl_t
) * abw
->abw_size
;
1843 buf
= mdb_zalloc(oldsize
<< 1, UM_SLEEP
);
1845 bcopy(abw
->abw_buf
, buf
, oldsize
);
1846 mdb_free(abw
->abw_buf
, oldsize
);
1848 abw
->abw_size
<<= 1;
1852 abw
->abw_buf
[abw
->abw_nbufs
].abb_addr
= addr
;
1853 abw
->abw_buf
[abw
->abw_nbufs
].abb_ts
= bcp
->bc_timestamp
;
1861 allocdby_walk_cache(uintptr_t addr
, const umem_cache_t
*c
, allocdby_walk_t
*abw
)
1863 if (mdb_pwalk(abw
->abw_walk
, (mdb_walk_cb_t
)allocdby_walk_bufctl
,
1865 mdb_warn("couldn't walk bufctl for cache %p", addr
);
1873 allocdby_cmp(const allocdby_bufctl_t
*lhs
, const allocdby_bufctl_t
*rhs
)
1875 if (lhs
->abb_ts
< rhs
->abb_ts
)
1877 if (lhs
->abb_ts
> rhs
->abb_ts
)
1883 allocdby_walk_init_common(mdb_walk_state_t
*wsp
, const char *walk
)
1885 allocdby_walk_t
*abw
;
1887 if (wsp
->walk_addr
== NULL
) {
1888 mdb_warn("allocdby walk doesn't support global walks\n");
1892 abw
= mdb_zalloc(sizeof (allocdby_walk_t
), UM_SLEEP
);
1894 abw
->abw_thread
= wsp
->walk_addr
;
1895 abw
->abw_walk
= walk
;
1896 abw
->abw_size
= 128; /* something reasonable */
1898 mdb_zalloc(abw
->abw_size
* sizeof (allocdby_bufctl_t
), UM_SLEEP
);
1900 wsp
->walk_data
= abw
;
1902 if (mdb_walk("umem_cache",
1903 (mdb_walk_cb_t
)allocdby_walk_cache
, abw
) == -1) {
1904 mdb_warn("couldn't walk umem_cache");
1905 allocdby_walk_fini(wsp
);
1909 qsort(abw
->abw_buf
, abw
->abw_nbufs
, sizeof (allocdby_bufctl_t
),
1910 (int(*)(const void *, const void *))allocdby_cmp
);
1916 allocdby_walk_init(mdb_walk_state_t
*wsp
)
1918 return (allocdby_walk_init_common(wsp
, "bufctl"));
1922 freedby_walk_init(mdb_walk_state_t
*wsp
)
1924 return (allocdby_walk_init_common(wsp
, "freectl"));
1928 allocdby_walk_step(mdb_walk_state_t
*wsp
)
1930 allocdby_walk_t
*abw
= wsp
->walk_data
;
1932 umem_bufctl_audit_t
*bcp
;
1933 UMEM_LOCAL_BUFCTL_AUDIT(&bcp
);
1935 if (abw
->abw_ndx
== abw
->abw_nbufs
)
1938 addr
= abw
->abw_buf
[abw
->abw_ndx
++].abb_addr
;
1940 if (mdb_vread(bcp
, UMEM_BUFCTL_AUDIT_SIZE
, addr
) == -1) {
1941 mdb_warn("couldn't read bufctl at %p", addr
);
1945 return (wsp
->walk_callback(addr
, bcp
, wsp
->walk_cbdata
));
1949 allocdby_walk_fini(mdb_walk_state_t
*wsp
)
1951 allocdby_walk_t
*abw
= wsp
->walk_data
;
1953 mdb_free(abw
->abw_buf
, sizeof (allocdby_bufctl_t
) * abw
->abw_size
);
1954 mdb_free(abw
, sizeof (allocdby_walk_t
));
1959 allocdby_walk(uintptr_t addr
, const umem_bufctl_audit_t
*bcp
, void *ignored
)
1961 char c
[MDB_SYM_NAMLEN
];
1965 mdb_printf("%0?p %12llx ", addr
, bcp
->bc_timestamp
);
1966 for (i
= 0; i
< bcp
->bc_depth
; i
++) {
1967 if (mdb_lookup_by_addr(bcp
->bc_stack
[i
],
1968 MDB_SYM_FUZZY
, c
, sizeof (c
), &sym
) == -1)
1970 if (is_umem_sym(c
, "umem_"))
1972 mdb_printf("%s+0x%lx",
1973 c
, bcp
->bc_stack
[i
] - (uintptr_t)sym
.st_value
);
1982 allocdby_common(uintptr_t addr
, uint_t flags
, const char *w
)
1984 if (!(flags
& DCMD_ADDRSPEC
))
1985 return (DCMD_USAGE
);
1987 mdb_printf("%-?s %12s %s\n", "BUFCTL", "TIMESTAMP", "CALLER");
1989 if (mdb_pwalk(w
, (mdb_walk_cb_t
)allocdby_walk
, NULL
, addr
) == -1) {
1990 mdb_warn("can't walk '%s' for %p", w
, addr
);
1999 allocdby(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2001 return (allocdby_common(addr
, flags
, "allocdby"));
2006 freedby(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2008 return (allocdby_common(addr
, flags
, "freedby"));
2011 typedef struct whatis_info
{
2013 const umem_cache_t
*wi_cache
;
2014 const vmem_t
*wi_vmem
;
2015 vmem_t
*wi_msb_arena
;
2016 size_t wi_slab_size
;
2021 /* call one of our dcmd functions with "-v" and the provided address */
2023 whatis_call_printer(mdb_dcmd_f
*dcmd
, uintptr_t addr
)
2026 a
.a_type
= MDB_TYPE_STRING
;
2027 a
.a_un
.a_str
= "-v";
2030 (void) (*dcmd
)(addr
, DCMD_ADDRSPEC
, 1, &a
);
2034 whatis_print_umem(whatis_info_t
*wi
, uintptr_t maddr
, uintptr_t addr
,
2037 mdb_whatis_t
*w
= wi
->wi_w
;
2038 const umem_cache_t
*cp
= wi
->wi_cache
;
2039 int quiet
= (mdb_whatis_flags(w
) & WHATIS_QUIET
);
2041 int call_printer
= (!quiet
&& (cp
->cache_flags
& UMF_AUDIT
));
2043 mdb_whatis_report_object(w
, maddr
, addr
, "");
2045 if (baddr
!= 0 && !call_printer
)
2046 mdb_printf("bufctl %p ", baddr
);
2048 mdb_printf("%s from %s",
2049 (wi
->wi_freemem
== FALSE
) ? "allocated" : "freed", cp
->cache_name
);
2051 if (call_printer
&& baddr
!= 0) {
2052 whatis_call_printer(bufctl
, baddr
);
2060 whatis_walk_umem(uintptr_t addr
, void *ignored
, whatis_info_t
*wi
)
2062 mdb_whatis_t
*w
= wi
->wi_w
;
2065 size_t size
= wi
->wi_cache
->cache_bufsize
;
2067 while (mdb_whatis_match(w
, addr
, size
, &cur
))
2068 whatis_print_umem(wi
, cur
, addr
, NULL
);
2070 return (WHATIS_WALKRET(w
));
2075 whatis_walk_bufctl(uintptr_t baddr
, const umem_bufctl_t
*bcp
, whatis_info_t
*wi
)
2077 mdb_whatis_t
*w
= wi
->wi_w
;
2080 uintptr_t addr
= (uintptr_t)bcp
->bc_addr
;
2081 size_t size
= wi
->wi_cache
->cache_bufsize
;
2083 while (mdb_whatis_match(w
, addr
, size
, &cur
))
2084 whatis_print_umem(wi
, cur
, addr
, baddr
);
2086 return (WHATIS_WALKRET(w
));
2091 whatis_walk_seg(uintptr_t addr
, const vmem_seg_t
*vs
, whatis_info_t
*wi
)
2093 mdb_whatis_t
*w
= wi
->wi_w
;
2095 size_t size
= vs
->vs_end
- vs
->vs_start
;
2098 /* We're not interested in anything but alloc and free segments */
2099 if (vs
->vs_type
!= VMEM_ALLOC
&& vs
->vs_type
!= VMEM_FREE
)
2102 while (mdb_whatis_match(w
, vs
->vs_start
, size
, &cur
)) {
2103 mdb_whatis_report_object(w
, cur
, vs
->vs_start
, "");
2106 * If we're not printing it seperately, provide the vmem_seg
2107 * pointer if it has a stack trace.
2109 if ((mdb_whatis_flags(w
) & WHATIS_QUIET
) &&
2110 ((mdb_whatis_flags(w
) & WHATIS_BUFCTL
) != 0 ||
2111 (vs
->vs_type
== VMEM_ALLOC
&& vs
->vs_depth
!= 0))) {
2112 mdb_printf("vmem_seg %p ", addr
);
2115 mdb_printf("%s from %s vmem arena",
2116 (vs
->vs_type
== VMEM_ALLOC
) ? "allocated" : "freed",
2117 wi
->wi_vmem
->vm_name
);
2119 if (!mdb_whatis_flags(w
) & WHATIS_QUIET
)
2120 whatis_call_printer(vmem_seg
, addr
);
2125 return (WHATIS_WALKRET(w
));
2129 whatis_walk_vmem(uintptr_t addr
, const vmem_t
*vmem
, whatis_info_t
*wi
)
2131 mdb_whatis_t
*w
= wi
->wi_w
;
2132 const char *nm
= vmem
->vm_name
;
2135 if (mdb_whatis_flags(w
) & WHATIS_VERBOSE
)
2136 mdb_printf("Searching vmem arena %s...\n", nm
);
2138 if (mdb_pwalk("vmem_seg",
2139 (mdb_walk_cb_t
)whatis_walk_seg
, wi
, addr
) == -1) {
2140 mdb_warn("can't walk vmem seg for %p", addr
);
2144 return (WHATIS_WALKRET(w
));
2149 whatis_walk_slab(uintptr_t saddr
, const umem_slab_t
*sp
, whatis_info_t
*wi
)
2151 mdb_whatis_t
*w
= wi
->wi_w
;
2153 /* It must overlap with the slab data, or it's not interesting */
2154 if (mdb_whatis_overlaps(w
,
2155 (uintptr_t)sp
->slab_base
, wi
->wi_slab_size
)) {
2156 wi
->wi_slab_found
++;
2163 whatis_walk_cache(uintptr_t addr
, const umem_cache_t
*c
, whatis_info_t
*wi
)
2165 mdb_whatis_t
*w
= wi
->wi_w
;
2166 char *walk
, *freewalk
;
2170 /* Override the '-b' flag as necessary */
2171 if (!(c
->cache_flags
& UMF_HASH
))
2172 do_bufctl
= FALSE
; /* no bufctls to walk */
2173 else if (c
->cache_flags
& UMF_AUDIT
)
2174 do_bufctl
= TRUE
; /* we always want debugging info */
2176 do_bufctl
= ((mdb_whatis_flags(w
) & WHATIS_BUFCTL
) != 0);
2180 freewalk
= "freectl";
2181 func
= (mdb_walk_cb_t
)whatis_walk_bufctl
;
2184 freewalk
= "freemem";
2185 func
= (mdb_walk_cb_t
)whatis_walk_umem
;
2190 if (mdb_whatis_flags(w
) & WHATIS_VERBOSE
)
2191 mdb_printf("Searching %s...\n", c
->cache_name
);
2194 * If more then two buffers live on each slab, figure out if we're
2195 * interested in anything in any slab before doing the more expensive
2196 * umem/freemem (bufctl/freectl) walkers.
2198 wi
->wi_slab_size
= c
->cache_slabsize
- c
->cache_maxcolor
;
2199 if (!(c
->cache_flags
& UMF_HASH
))
2200 wi
->wi_slab_size
-= sizeof (umem_slab_t
);
2202 if ((wi
->wi_slab_size
/ c
->cache_chunksize
) > 2) {
2203 wi
->wi_slab_found
= 0;
2204 if (mdb_pwalk("umem_slab", (mdb_walk_cb_t
)whatis_walk_slab
, wi
,
2206 mdb_warn("can't find umem_slab walker");
2209 if (wi
->wi_slab_found
== 0)
2213 wi
->wi_freemem
= FALSE
;
2214 if (mdb_pwalk(walk
, func
, wi
, addr
) == -1) {
2215 mdb_warn("can't find %s walker", walk
);
2219 if (mdb_whatis_done(w
))
2223 * We have searched for allocated memory; now search for freed memory.
2225 if (mdb_whatis_flags(w
) & WHATIS_VERBOSE
)
2226 mdb_printf("Searching %s for free memory...\n", c
->cache_name
);
2228 wi
->wi_freemem
= TRUE
;
2230 if (mdb_pwalk(freewalk
, func
, wi
, addr
) == -1) {
2231 mdb_warn("can't find %s walker", freewalk
);
2235 return (WHATIS_WALKRET(w
));
2239 whatis_walk_touch(uintptr_t addr
, const umem_cache_t
*c
, whatis_info_t
*wi
)
2241 if (c
->cache_arena
== wi
->wi_msb_arena
||
2242 (c
->cache_cflags
& UMC_NOTOUCH
))
2245 return (whatis_walk_cache(addr
, c
, wi
));
2249 whatis_walk_metadata(uintptr_t addr
, const umem_cache_t
*c
, whatis_info_t
*wi
)
2251 if (c
->cache_arena
!= wi
->wi_msb_arena
)
2254 return (whatis_walk_cache(addr
, c
, wi
));
2258 whatis_walk_notouch(uintptr_t addr
, const umem_cache_t
*c
, whatis_info_t
*wi
)
2260 if (c
->cache_arena
== wi
->wi_msb_arena
||
2261 !(c
->cache_cflags
& UMC_NOTOUCH
))
2264 return (whatis_walk_cache(addr
, c
, wi
));
2269 whatis_run_umem(mdb_whatis_t
*w
, void *ignored
)
2273 bzero(&wi
, sizeof (wi
));
2276 /* umem's metadata is allocated from the umem_internal_arena */
2277 if (umem_readvar(&wi
.wi_msb_arena
, "umem_internal_arena") == -1)
2278 mdb_warn("unable to readvar \"umem_internal_arena\"");
2281 * We process umem caches in the following order:
2283 * non-UMC_NOTOUCH, non-metadata (typically the most interesting)
2284 * metadata (can be huge with UMF_AUDIT)
2285 * UMC_NOTOUCH, non-metadata (see umem_walk_all())
2287 if (mdb_walk("umem_cache", (mdb_walk_cb_t
)whatis_walk_touch
,
2289 mdb_walk("umem_cache", (mdb_walk_cb_t
)whatis_walk_metadata
,
2291 mdb_walk("umem_cache", (mdb_walk_cb_t
)whatis_walk_notouch
,
2293 mdb_warn("couldn't find umem_cache walker");
2301 whatis_run_vmem(mdb_whatis_t
*w
, void *ignored
)
2305 bzero(&wi
, sizeof (wi
));
2308 if (mdb_walk("vmem_postfix",
2309 (mdb_walk_cb_t
)whatis_walk_vmem
, &wi
) == -1) {
2310 mdb_warn("couldn't find vmem_postfix walker");
2320 "umem_cache", "walk list of umem caches", umem_cache_walk_init
,
2321 umem_cache_walk_step
, umem_cache_walk_fini
2324 if (mdb_add_walker(&w
) == -1) {
2325 mdb_warn("failed to add umem_cache walker");
2329 if (umem_update_variables() == -1)
2332 /* install a callback so that our variables are always up-to-date */
2333 (void) mdb_callback_add(MDB_CALLBACK_STCHG
, umem_statechange_cb
, NULL
);
2334 umem_statechange_cb(NULL
);
2337 * Register our ::whatis callbacks.
2339 mdb_whatis_register("umem", whatis_run_umem
, NULL
,
2340 WHATIS_PRIO_ALLOCATOR
, WHATIS_REG_NO_ID
);
2341 mdb_whatis_register("vmem", whatis_run_vmem
, NULL
,
2342 WHATIS_PRIO_ALLOCATOR
, WHATIS_REG_NO_ID
);
2347 typedef struct umem_log_cpu
{
2353 umem_log_walk(uintptr_t addr
, const umem_bufctl_audit_t
*b
, umem_log_cpu_t
*umc
)
2357 for (i
= 0; i
< umem_max_ncpus
; i
++) {
2358 if (addr
>= umc
[i
].umc_low
&& addr
< umc
[i
].umc_high
)
2362 if (i
== umem_max_ncpus
)
2365 mdb_printf("%3d", i
);
2367 mdb_printf(" %0?p %0?p %16llx %0?p\n", addr
, b
->bc_addr
,
2368 b
->bc_timestamp
, b
->bc_thread
);
2375 umem_log(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2377 umem_log_header_t lh
;
2378 umem_cpu_log_header_t clh
;
2379 uintptr_t lhp
, clhp
;
2380 umem_log_cpu_t
*umc
;
2383 if (umem_readvar(&lhp
, "umem_transaction_log") == -1) {
2384 mdb_warn("failed to read 'umem_transaction_log'");
2389 mdb_warn("no umem transaction log\n");
2393 if (mdb_vread(&lh
, sizeof (umem_log_header_t
), lhp
) == -1) {
2394 mdb_warn("failed to read log header at %p", lhp
);
2398 clhp
= lhp
+ ((uintptr_t)&lh
.lh_cpu
[0] - (uintptr_t)&lh
);
2400 umc
= mdb_zalloc(sizeof (umem_log_cpu_t
) * umem_max_ncpus
,
2403 for (i
= 0; i
< umem_max_ncpus
; i
++) {
2404 if (mdb_vread(&clh
, sizeof (clh
), clhp
) == -1) {
2405 mdb_warn("cannot read cpu %d's log header at %p",
2410 umc
[i
].umc_low
= clh
.clh_chunk
* lh
.lh_chunksize
+
2411 (uintptr_t)lh
.lh_base
;
2412 umc
[i
].umc_high
= (uintptr_t)clh
.clh_current
;
2414 clhp
+= sizeof (umem_cpu_log_header_t
);
2417 if (DCMD_HDRSPEC(flags
)) {
2418 mdb_printf("%3s %-?s %-?s %16s %-?s\n", "CPU", "ADDR",
2419 "BUFADDR", "TIMESTAMP", "THREAD");
2423 * If we have been passed an address, we'll just print out that
2426 if (flags
& DCMD_ADDRSPEC
) {
2427 umem_bufctl_audit_t
*bp
;
2428 UMEM_LOCAL_BUFCTL_AUDIT(&bp
);
2430 if (mdb_vread(bp
, UMEM_BUFCTL_AUDIT_SIZE
, addr
) == -1) {
2431 mdb_warn("failed to read bufctl at %p", addr
);
2435 (void) umem_log_walk(addr
, bp
, umc
);
2440 if (mdb_walk("umem_log", (mdb_walk_cb_t
)umem_log_walk
, umc
) == -1) {
2441 mdb_warn("can't find umem log walker");
2448 typedef struct bufctl_history_cb
{
2451 const mdb_arg_t
*bhc_argv
;
2453 } bufctl_history_cb_t
;
2457 bufctl_history_callback(uintptr_t addr
, const void *ign
, void *arg
)
2459 bufctl_history_cb_t
*bhc
= arg
;
2462 bufctl(addr
, bhc
->bhc_flags
, bhc
->bhc_argc
, bhc
->bhc_argv
);
2464 bhc
->bhc_flags
&= ~DCMD_LOOPFIRST
;
2466 return ((bhc
->bhc_ret
== DCMD_OK
)? WALK_NEXT
: WALK_DONE
);
2473 "Display the contents of umem_bufctl_audit_ts, with optional filtering.\n");
2475 mdb_printf("%<b>OPTIONS%</b>\n");
2478 " -v Display the full content of the bufctl, including its stack trace\n"
2479 " -h retrieve the bufctl's transaction history, if available\n"
2481 " filter out bufctls not involving the buffer at addr\n"
2483 " filter out bufctls without the function/PC in their stack trace\n"
2485 " filter out bufctls timestamped before earliest\n"
2487 " filter out bufctls timestamped after latest\n"
2489 " filter out bufctls not involving thread\n");
2493 bufctl(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2495 uint_t verbose
= FALSE
;
2496 uint_t history
= FALSE
;
2497 uint_t in_history
= FALSE
;
2498 uintptr_t caller
= NULL
, thread
= NULL
;
2499 uintptr_t laddr
, haddr
, baddr
= NULL
;
2500 hrtime_t earliest
= 0, latest
= 0;
2502 char c
[MDB_SYM_NAMLEN
];
2504 umem_bufctl_audit_t
*bcp
;
2505 UMEM_LOCAL_BUFCTL_AUDIT(&bcp
);
2507 if (mdb_getopts(argc
, argv
,
2508 'v', MDB_OPT_SETBITS
, TRUE
, &verbose
,
2509 'h', MDB_OPT_SETBITS
, TRUE
, &history
,
2510 'H', MDB_OPT_SETBITS
, TRUE
, &in_history
, /* internal */
2511 'c', MDB_OPT_UINTPTR
, &caller
,
2512 't', MDB_OPT_UINTPTR
, &thread
,
2513 'e', MDB_OPT_UINT64
, &earliest
,
2514 'l', MDB_OPT_UINT64
, &latest
,
2515 'a', MDB_OPT_UINTPTR
, &baddr
, NULL
) != argc
)
2516 return (DCMD_USAGE
);
2518 if (!(flags
& DCMD_ADDRSPEC
))
2519 return (DCMD_USAGE
);
2521 if (in_history
&& !history
)
2522 return (DCMD_USAGE
);
2524 if (history
&& !in_history
) {
2525 mdb_arg_t
*nargv
= mdb_zalloc(sizeof (*nargv
) * (argc
+ 1),
2527 bufctl_history_cb_t bhc
;
2529 nargv
[0].a_type
= MDB_TYPE_STRING
;
2530 nargv
[0].a_un
.a_str
= "-H"; /* prevent recursion */
2532 for (i
= 0; i
< argc
; i
++)
2533 nargv
[i
+ 1] = argv
[i
];
2536 * When in history mode, we treat each element as if it
2537 * were in a seperate loop, so that the headers group
2538 * bufctls with similar histories.
2540 bhc
.bhc_flags
= flags
| DCMD_LOOP
| DCMD_LOOPFIRST
;
2541 bhc
.bhc_argc
= argc
+ 1;
2542 bhc
.bhc_argv
= nargv
;
2543 bhc
.bhc_ret
= DCMD_OK
;
2545 if (mdb_pwalk("bufctl_history", bufctl_history_callback
, &bhc
,
2547 mdb_warn("unable to walk bufctl_history");
2551 if (bhc
.bhc_ret
== DCMD_OK
&& !(flags
& DCMD_PIPE_OUT
))
2554 return (bhc
.bhc_ret
);
2557 if (DCMD_HDRSPEC(flags
) && !(flags
& DCMD_PIPE_OUT
)) {
2559 mdb_printf("%16s %16s %16s %16s\n"
2560 "%<u>%16s %16s %16s %16s%</u>\n",
2561 "ADDR", "BUFADDR", "TIMESTAMP", "THREAD",
2562 "", "CACHE", "LASTLOG", "CONTENTS");
2564 mdb_printf("%<u>%-?s %-?s %-12s %5s %s%</u>\n",
2565 "ADDR", "BUFADDR", "TIMESTAMP", "THRD", "CALLER");
2569 if (mdb_vread(bcp
, UMEM_BUFCTL_AUDIT_SIZE
, addr
) == -1) {
2570 mdb_warn("couldn't read bufctl at %p", addr
);
2575 * Guard against bogus bc_depth in case the bufctl is corrupt or
2576 * the address does not really refer to a bufctl.
2578 depth
= MIN(bcp
->bc_depth
, umem_stack_depth
);
2580 if (caller
!= NULL
) {
2582 haddr
= caller
+ sizeof (caller
);
2584 if (mdb_lookup_by_addr(caller
, MDB_SYM_FUZZY
, c
, sizeof (c
),
2585 &sym
) != -1 && caller
== (uintptr_t)sym
.st_value
) {
2587 * We were provided an exact symbol value; any
2588 * address in the function is valid.
2590 laddr
= (uintptr_t)sym
.st_value
;
2591 haddr
= (uintptr_t)sym
.st_value
+ sym
.st_size
;
2594 for (i
= 0; i
< depth
; i
++)
2595 if (bcp
->bc_stack
[i
] >= laddr
&&
2596 bcp
->bc_stack
[i
] < haddr
)
2603 if (thread
!= NULL
&& (uintptr_t)bcp
->bc_thread
!= thread
)
2606 if (earliest
!= 0 && bcp
->bc_timestamp
< earliest
)
2609 if (latest
!= 0 && bcp
->bc_timestamp
> latest
)
2612 if (baddr
!= 0 && (uintptr_t)bcp
->bc_addr
!= baddr
)
2615 if (flags
& DCMD_PIPE_OUT
) {
2616 mdb_printf("%#r\n", addr
);
2622 "%<b>%16p%</b> %16p %16llx %16d\n"
2623 "%16s %16p %16p %16p\n",
2624 addr
, bcp
->bc_addr
, bcp
->bc_timestamp
, bcp
->bc_thread
,
2625 "", bcp
->bc_cache
, bcp
->bc_lastlog
, bcp
->bc_contents
);
2628 for (i
= 0; i
< depth
; i
++)
2629 mdb_printf("%a\n", bcp
->bc_stack
[i
]);
2633 mdb_printf("%0?p %0?p %12llx %5d", addr
, bcp
->bc_addr
,
2634 bcp
->bc_timestamp
, bcp
->bc_thread
);
2636 for (i
= 0; i
< depth
; i
++) {
2637 if (mdb_lookup_by_addr(bcp
->bc_stack
[i
],
2638 MDB_SYM_FUZZY
, c
, sizeof (c
), &sym
) == -1)
2640 if (is_umem_sym(c
, "umem_"))
2642 mdb_printf(" %a\n", bcp
->bc_stack
[i
]);
2655 bufctl_audit(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2659 if (!(flags
& DCMD_ADDRSPEC
))
2660 return (DCMD_USAGE
);
2663 return (DCMD_USAGE
);
2665 a
.a_type
= MDB_TYPE_STRING
;
2666 a
.a_un
.a_str
= "-v";
2668 return (bufctl(addr
, flags
, 1, &a
));
2671 typedef struct umem_verify
{
2672 uint64_t *umv_buf
; /* buffer to read cache contents into */
2673 size_t umv_size
; /* number of bytes in umv_buf */
2674 int umv_corruption
; /* > 0 if corruption found. */
2675 int umv_besilent
; /* report actual corruption sites */
2676 struct umem_cache umv_cache
; /* the cache we're operating on */
2681 * verify that buf is filled with the pattern pat.
2684 verify_pattern(uint64_t *buf_arg
, size_t size
, uint64_t pat
)
2687 uint64_t *bufend
= (uint64_t *)((char *)buf_arg
+ size
);
2690 for (buf
= buf_arg
; buf
< bufend
; buf
++)
2692 return ((uintptr_t)buf
- (uintptr_t)buf_arg
);
2698 * verify that btp->bt_bxstat == (bcp ^ pat)
2701 verify_buftag(umem_buftag_t
*btp
, uintptr_t pat
)
2703 return (btp
->bt_bxstat
== ((intptr_t)btp
->bt_bufctl
^ pat
) ? 0 : -1);
2708 * verify the integrity of a free block of memory by checking
2709 * that it is filled with 0xdeadbeef and that its buftag is sane.
2713 verify_free(uintptr_t addr
, const void *data
, void *private)
2715 umem_verify_t
*umv
= (umem_verify_t
*)private;
2716 uint64_t *buf
= umv
->umv_buf
; /* buf to validate */
2717 int64_t corrupt
; /* corruption offset */
2718 umem_buftag_t
*buftagp
; /* ptr to buftag */
2719 umem_cache_t
*cp
= &umv
->umv_cache
;
2720 int besilent
= umv
->umv_besilent
;
2723 buftagp
= UMEM_BUFTAG(cp
, buf
);
2726 * Read the buffer to check.
2728 if (mdb_vread(buf
, umv
->umv_size
, addr
) == -1) {
2730 mdb_warn("couldn't read %p", addr
);
2734 if ((corrupt
= verify_pattern(buf
, cp
->cache_verify
,
2735 UMEM_FREE_PATTERN
)) >= 0) {
2737 mdb_printf("buffer %p (free) seems corrupted, at %p\n",
2738 addr
, (uintptr_t)addr
+ corrupt
);
2742 if ((cp
->cache_flags
& UMF_HASH
) &&
2743 buftagp
->bt_redzone
!= UMEM_REDZONE_PATTERN
) {
2745 mdb_printf("buffer %p (free) seems to "
2746 "have a corrupt redzone pattern\n", addr
);
2751 * confirm bufctl pointer integrity.
2753 if (verify_buftag(buftagp
, UMEM_BUFTAG_FREE
) == -1) {
2755 mdb_printf("buffer %p (free) has a corrupt "
2762 umv
->umv_corruption
++;
2768 * Verify that the buftag of an allocated buffer makes sense with respect
2773 verify_alloc(uintptr_t addr
, const void *data
, void *private)
2775 umem_verify_t
*umv
= (umem_verify_t
*)private;
2776 umem_cache_t
*cp
= &umv
->umv_cache
;
2777 uint64_t *buf
= umv
->umv_buf
; /* buf to validate */
2779 umem_buftag_t
*buftagp
= UMEM_BUFTAG(cp
, buf
);
2780 uint32_t *ip
= (uint32_t *)buftagp
;
2781 uint8_t *bp
= (uint8_t *)buf
;
2782 int looks_ok
= 0, size_ok
= 1; /* flags for finding corruption */
2783 int besilent
= umv
->umv_besilent
;
2786 * Read the buffer to check.
2788 if (mdb_vread(buf
, umv
->umv_size
, addr
) == -1) {
2790 mdb_warn("couldn't read %p", addr
);
2795 * There are two cases to handle:
2796 * 1. If the buf was alloc'd using umem_cache_alloc, it will have
2797 * 0xfeedfacefeedface at the end of it
2798 * 2. If the buf was alloc'd using umem_alloc, it will have
2799 * 0xbb just past the end of the region in use. At the buftag,
2800 * it will have 0xfeedface (or, if the whole buffer is in use,
2801 * 0xfeedface & bb000000 or 0xfeedfacf & 000000bb depending on
2802 * endianness), followed by 32 bits containing the offset of the
2803 * 0xbb byte in the buffer.
2805 * Finally, the two 32-bit words that comprise the second half of the
2806 * buftag should xor to UMEM_BUFTAG_ALLOC
2809 if (buftagp
->bt_redzone
== UMEM_REDZONE_PATTERN
)
2811 else if (!UMEM_SIZE_VALID(ip
[1]))
2813 else if (bp
[UMEM_SIZE_DECODE(ip
[1])] == UMEM_REDZONE_BYTE
)
2820 mdb_printf("buffer %p (allocated) has a corrupt "
2821 "redzone size encoding\n", addr
);
2827 mdb_printf("buffer %p (allocated) has a corrupt "
2828 "redzone signature\n", addr
);
2832 if (verify_buftag(buftagp
, UMEM_BUFTAG_ALLOC
) == -1) {
2834 mdb_printf("buffer %p (allocated) has a "
2835 "corrupt buftag\n", addr
);
2841 umv
->umv_corruption
++;
2847 umem_verify(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2849 if (flags
& DCMD_ADDRSPEC
) {
2850 int check_alloc
= 0, check_free
= 0;
2853 if (mdb_vread(&umv
.umv_cache
, sizeof (umv
.umv_cache
),
2855 mdb_warn("couldn't read umem_cache %p", addr
);
2859 umv
.umv_size
= umv
.umv_cache
.cache_buftag
+
2860 sizeof (umem_buftag_t
);
2861 umv
.umv_buf
= mdb_alloc(umv
.umv_size
, UM_SLEEP
| UM_GC
);
2862 umv
.umv_corruption
= 0;
2864 if ((umv
.umv_cache
.cache_flags
& UMF_REDZONE
)) {
2866 if (umv
.umv_cache
.cache_flags
& UMF_DEADBEEF
)
2869 if (!(flags
& DCMD_LOOP
)) {
2870 mdb_warn("cache %p (%s) does not have "
2871 "redzone checking enabled\n", addr
,
2872 umv
.umv_cache
.cache_name
);
2877 if (flags
& DCMD_LOOP
) {
2879 * table mode, don't print out every corrupt buffer
2881 umv
.umv_besilent
= 1;
2883 mdb_printf("Summary for cache '%s'\n",
2884 umv
.umv_cache
.cache_name
);
2886 umv
.umv_besilent
= 0;
2890 (void) mdb_pwalk("umem", verify_alloc
, &umv
, addr
);
2892 (void) mdb_pwalk("freemem", verify_free
, &umv
, addr
);
2894 if (flags
& DCMD_LOOP
) {
2895 if (umv
.umv_corruption
== 0) {
2896 mdb_printf("%-*s %?p clean\n",
2898 umv
.umv_cache
.cache_name
, addr
);
2900 char *s
= ""; /* optional s in "buffer[s]" */
2901 if (umv
.umv_corruption
> 1)
2904 mdb_printf("%-*s %?p %d corrupt buffer%s\n",
2906 umv
.umv_cache
.cache_name
, addr
,
2907 umv
.umv_corruption
, s
);
2911 * This is the more verbose mode, when the user has
2912 * type addr::umem_verify. If the cache was clean,
2913 * nothing will have yet been printed. So say something.
2915 if (umv
.umv_corruption
== 0)
2916 mdb_printf("clean\n");
2922 * If the user didn't specify a cache to verify, we'll walk all
2923 * umem_cache's, specifying ourself as a callback for each...
2924 * this is the equivalent of '::walk umem_cache .::umem_verify'
2926 mdb_printf("%<u>%-*s %-?s %-20s%</b>\n", UMEM_CACHE_NAMELEN
,
2927 "Cache Name", "Addr", "Cache Integrity");
2928 (void) (mdb_walk_dcmd("umem_cache", "umem_verify", 0, NULL
));
2934 typedef struct vmem_node
{
2935 struct vmem_node
*vn_next
;
2936 struct vmem_node
*vn_parent
;
2937 struct vmem_node
*vn_sibling
;
2938 struct vmem_node
*vn_children
;
2944 typedef struct vmem_walk
{
2945 vmem_node_t
*vw_root
;
2946 vmem_node_t
*vw_current
;
2950 vmem_walk_init(mdb_walk_state_t
*wsp
)
2952 uintptr_t vaddr
, paddr
;
2953 vmem_node_t
*head
= NULL
, *root
= NULL
, *current
= NULL
, *parent
, *vp
;
2956 if (umem_readvar(&vaddr
, "vmem_list") == -1) {
2957 mdb_warn("couldn't read 'vmem_list'");
2961 while (vaddr
!= NULL
) {
2962 vp
= mdb_zalloc(sizeof (vmem_node_t
), UM_SLEEP
);
2963 vp
->vn_addr
= vaddr
;
2967 if (vaddr
== wsp
->walk_addr
)
2970 if (mdb_vread(&vp
->vn_vmem
, sizeof (vmem_t
), vaddr
) == -1) {
2971 mdb_warn("couldn't read vmem_t at %p", vaddr
);
2975 vaddr
= (uintptr_t)vp
->vn_vmem
.vm_next
;
2978 for (vp
= head
; vp
!= NULL
; vp
= vp
->vn_next
) {
2980 if ((paddr
= (uintptr_t)vp
->vn_vmem
.vm_source
) == NULL
) {
2981 vp
->vn_sibling
= root
;
2986 for (parent
= head
; parent
!= NULL
; parent
= parent
->vn_next
) {
2987 if (parent
->vn_addr
!= paddr
)
2989 vp
->vn_sibling
= parent
->vn_children
;
2990 parent
->vn_children
= vp
;
2991 vp
->vn_parent
= parent
;
2995 if (parent
== NULL
) {
2996 mdb_warn("couldn't find %p's parent (%p)\n",
2997 vp
->vn_addr
, paddr
);
3002 vw
= mdb_zalloc(sizeof (vmem_walk_t
), UM_SLEEP
);
3005 if (current
!= NULL
)
3006 vw
->vw_current
= current
;
3008 vw
->vw_current
= root
;
3010 wsp
->walk_data
= vw
;
3013 for (vp
= head
; head
!= NULL
; vp
= head
) {
3015 mdb_free(vp
, sizeof (vmem_node_t
));
3022 vmem_walk_step(mdb_walk_state_t
*wsp
)
3024 vmem_walk_t
*vw
= wsp
->walk_data
;
3028 if ((vp
= vw
->vw_current
) == NULL
)
3031 rval
= wsp
->walk_callback(vp
->vn_addr
, &vp
->vn_vmem
, wsp
->walk_cbdata
);
3033 if (vp
->vn_children
!= NULL
) {
3034 vw
->vw_current
= vp
->vn_children
;
3039 vw
->vw_current
= vp
->vn_sibling
;
3041 } while (vw
->vw_current
== NULL
&& vp
!= NULL
);
3047 * The "vmem_postfix" walk walks the vmem arenas in post-fix order; all
3048 * children are visited before their parent. We perform the postfix walk
3049 * iteratively (rather than recursively) to allow mdb to regain control
3050 * after each callback.
3053 vmem_postfix_walk_step(mdb_walk_state_t
*wsp
)
3055 vmem_walk_t
*vw
= wsp
->walk_data
;
3056 vmem_node_t
*vp
= vw
->vw_current
;
3060 * If this node is marked, then we know that we have already visited
3061 * all of its children. If the node has any siblings, they need to
3062 * be visited next; otherwise, we need to visit the parent. Note
3063 * that vp->vn_marked will only be zero on the first invocation of
3064 * the step function.
3066 if (vp
->vn_marked
) {
3067 if (vp
->vn_sibling
!= NULL
)
3068 vp
= vp
->vn_sibling
;
3069 else if (vp
->vn_parent
!= NULL
)
3073 * We have neither a parent, nor a sibling, and we
3074 * have already been visited; we're done.
3081 * Before we visit this node, visit its children.
3083 while (vp
->vn_children
!= NULL
&& !vp
->vn_children
->vn_marked
)
3084 vp
= vp
->vn_children
;
3087 vw
->vw_current
= vp
;
3088 rval
= wsp
->walk_callback(vp
->vn_addr
, &vp
->vn_vmem
, wsp
->walk_cbdata
);
3094 vmem_walk_fini(mdb_walk_state_t
*wsp
)
3096 vmem_walk_t
*vw
= wsp
->walk_data
;
3097 vmem_node_t
*root
= vw
->vw_root
;
3103 if ((vw
->vw_root
= root
->vn_children
) != NULL
)
3104 vmem_walk_fini(wsp
);
3106 vw
->vw_root
= root
->vn_sibling
;
3107 done
= (root
->vn_sibling
== NULL
&& root
->vn_parent
== NULL
);
3108 mdb_free(root
, sizeof (vmem_node_t
));
3111 mdb_free(vw
, sizeof (vmem_walk_t
));
3113 vmem_walk_fini(wsp
);
3117 typedef struct vmem_seg_walk
{
3119 uintptr_t vsw_start
;
3120 uintptr_t vsw_current
;
3125 vmem_seg_walk_common_init(mdb_walk_state_t
*wsp
, uint8_t type
, char *name
)
3127 vmem_seg_walk_t
*vsw
;
3129 if (wsp
->walk_addr
== NULL
) {
3130 mdb_warn("vmem_%s does not support global walks\n", name
);
3134 wsp
->walk_data
= vsw
= mdb_alloc(sizeof (vmem_seg_walk_t
), UM_SLEEP
);
3136 vsw
->vsw_type
= type
;
3137 vsw
->vsw_start
= wsp
->walk_addr
+ OFFSETOF(vmem_t
, vm_seg0
);
3138 vsw
->vsw_current
= vsw
->vsw_start
;
3144 * vmem segments can't have type 0 (this should be added to vmem_impl.h).
3149 vmem_alloc_walk_init(mdb_walk_state_t
*wsp
)
3151 return (vmem_seg_walk_common_init(wsp
, VMEM_ALLOC
, "alloc"));
3155 vmem_free_walk_init(mdb_walk_state_t
*wsp
)
3157 return (vmem_seg_walk_common_init(wsp
, VMEM_FREE
, "free"));
3161 vmem_span_walk_init(mdb_walk_state_t
*wsp
)
3163 return (vmem_seg_walk_common_init(wsp
, VMEM_SPAN
, "span"));
3167 vmem_seg_walk_init(mdb_walk_state_t
*wsp
)
3169 return (vmem_seg_walk_common_init(wsp
, VMEM_NONE
, "seg"));
3173 vmem_seg_walk_step(mdb_walk_state_t
*wsp
)
3176 vmem_seg_walk_t
*vsw
= wsp
->walk_data
;
3177 uintptr_t addr
= vsw
->vsw_current
;
3178 static size_t seg_size
= 0;
3182 if (umem_readvar(&seg_size
, "vmem_seg_size") == -1) {
3183 mdb_warn("failed to read 'vmem_seg_size'");
3184 seg_size
= sizeof (vmem_seg_t
);
3188 if (seg_size
< sizeof (seg
))
3189 bzero((caddr_t
)&seg
+ seg_size
, sizeof (seg
) - seg_size
);
3191 if (mdb_vread(&seg
, seg_size
, addr
) == -1) {
3192 mdb_warn("couldn't read vmem_seg at %p", addr
);
3196 vsw
->vsw_current
= (uintptr_t)seg
.vs_anext
;
3197 if (vsw
->vsw_type
!= VMEM_NONE
&& seg
.vs_type
!= vsw
->vsw_type
) {
3200 rval
= wsp
->walk_callback(addr
, &seg
, wsp
->walk_cbdata
);
3203 if (vsw
->vsw_current
== vsw
->vsw_start
)
3210 vmem_seg_walk_fini(mdb_walk_state_t
*wsp
)
3212 vmem_seg_walk_t
*vsw
= wsp
->walk_data
;
3214 mdb_free(vsw
, sizeof (vmem_seg_walk_t
));
3217 #define VMEM_NAMEWIDTH 22
3220 vmem(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
3225 char c
[VMEM_NAMEWIDTH
];
3227 if (!(flags
& DCMD_ADDRSPEC
)) {
3228 if (mdb_walk_dcmd("vmem", "vmem", argc
, argv
) == -1) {
3229 mdb_warn("can't walk vmem");
3235 if (DCMD_HDRSPEC(flags
))
3236 mdb_printf("%-?s %-*s %10s %12s %9s %5s\n",
3237 "ADDR", VMEM_NAMEWIDTH
, "NAME", "INUSE",
3238 "TOTAL", "SUCCEED", "FAIL");
3240 if (mdb_vread(&v
, sizeof (v
), addr
) == -1) {
3241 mdb_warn("couldn't read vmem at %p", addr
);
3245 for (paddr
= (uintptr_t)v
.vm_source
; paddr
!= NULL
; ident
+= 2) {
3246 if (mdb_vread(&parent
, sizeof (parent
), paddr
) == -1) {
3247 mdb_warn("couldn't trace %p's ancestry", addr
);
3251 paddr
= (uintptr_t)parent
.vm_source
;
3254 (void) mdb_snprintf(c
, VMEM_NAMEWIDTH
, "%*s%s", ident
, "", v
.vm_name
);
3256 mdb_printf("%0?p %-*s %10llu %12llu %9llu %5llu\n",
3257 addr
, VMEM_NAMEWIDTH
, c
,
3258 v
.vm_kstat
.vk_mem_inuse
, v
.vm_kstat
.vk_mem_total
,
3259 v
.vm_kstat
.vk_alloc
, v
.vm_kstat
.vk_fail
);
3268 "Display the contents of vmem_seg_ts, with optional filtering.\n"
3270 "A vmem_seg_t represents a range of addresses (or arbitrary numbers),\n"
3271 "representing a single chunk of data. Only ALLOC segments have debugging\n"
3274 mdb_printf("%<b>OPTIONS%</b>\n");
3277 " -v Display the full content of the vmem_seg, including its stack trace\n"
3278 " -s report the size of the segment, instead of the end address\n"
3280 " filter out segments without the function/PC in their stack trace\n"
3282 " filter out segments timestamped before earliest\n"
3284 " filter out segments timestamped after latest\n"
3286 " filer out segments smaller than minsize\n"
3288 " filer out segments larger than maxsize\n"
3290 " filter out segments not involving thread\n"
3292 " filter out segments not of type 'type'\n"
3293 " type is one of: ALLOC/FREE/SPAN/ROTOR/WALKER\n");
3299 vmem_seg(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
3302 uintptr_t *stk
= vs
.vs_stack
;
3305 const char *type
= NULL
;
3307 char c
[MDB_SYM_NAMLEN
];
3311 uintptr_t laddr
, haddr
;
3313 uintptr_t caller
= NULL
, thread
= NULL
;
3314 uintptr_t minsize
= 0, maxsize
= 0;
3316 hrtime_t earliest
= 0, latest
= 0;
3321 if (!(flags
& DCMD_ADDRSPEC
))
3322 return (DCMD_USAGE
);
3324 if (mdb_getopts(argc
, argv
,
3325 'c', MDB_OPT_UINTPTR
, &caller
,
3326 'e', MDB_OPT_UINT64
, &earliest
,
3327 'l', MDB_OPT_UINT64
, &latest
,
3328 's', MDB_OPT_SETBITS
, TRUE
, &size
,
3329 'm', MDB_OPT_UINTPTR
, &minsize
,
3330 'M', MDB_OPT_UINTPTR
, &maxsize
,
3331 't', MDB_OPT_UINTPTR
, &thread
,
3332 'T', MDB_OPT_STR
, &type
,
3333 'v', MDB_OPT_SETBITS
, TRUE
, &verbose
,
3335 return (DCMD_USAGE
);
3337 if (DCMD_HDRSPEC(flags
) && !(flags
& DCMD_PIPE_OUT
)) {
3339 mdb_printf("%16s %4s %16s %16s %16s\n"
3340 "%<u>%16s %4s %16s %16s %16s%</u>\n",
3341 "ADDR", "TYPE", "START", "END", "SIZE",
3342 "", "", "THREAD", "TIMESTAMP", "");
3344 mdb_printf("%?s %4s %?s %?s %s\n", "ADDR", "TYPE",
3345 "START", size
? "SIZE" : "END", "WHO");
3349 if (mdb_vread(&vs
, sizeof (vs
), addr
) == -1) {
3350 mdb_warn("couldn't read vmem_seg at %p", addr
);
3355 if (strcmp(type
, "ALLC") == 0 || strcmp(type
, "ALLOC") == 0)
3357 else if (strcmp(type
, "FREE") == 0)
3359 else if (strcmp(type
, "SPAN") == 0)
3361 else if (strcmp(type
, "ROTR") == 0 ||
3362 strcmp(type
, "ROTOR") == 0)
3364 else if (strcmp(type
, "WLKR") == 0 ||
3365 strcmp(type
, "WALKER") == 0)
3368 mdb_warn("\"%s\" is not a recognized vmem_seg type\n",
3373 if (vs
.vs_type
!= t
)
3377 sz
= vs
.vs_end
- vs
.vs_start
;
3379 if (minsize
!= 0 && sz
< minsize
)
3382 if (maxsize
!= 0 && sz
> maxsize
)
3386 depth
= vs
.vs_depth
;
3389 * debug info, when present, is only accurate for VMEM_ALLOC segments
3391 no_debug
= (t
!= VMEM_ALLOC
) ||
3392 (depth
== 0 || depth
> VMEM_STACK_DEPTH
);
3395 if (caller
!= NULL
|| thread
!= NULL
|| earliest
!= 0 ||
3397 return (DCMD_OK
); /* not enough info */
3399 if (caller
!= NULL
) {
3401 haddr
= caller
+ sizeof (caller
);
3403 if (mdb_lookup_by_addr(caller
, MDB_SYM_FUZZY
, c
,
3404 sizeof (c
), &sym
) != -1 &&
3405 caller
== (uintptr_t)sym
.st_value
) {
3407 * We were provided an exact symbol value; any
3408 * address in the function is valid.
3410 laddr
= (uintptr_t)sym
.st_value
;
3411 haddr
= (uintptr_t)sym
.st_value
+ sym
.st_size
;
3414 for (i
= 0; i
< depth
; i
++)
3415 if (vs
.vs_stack
[i
] >= laddr
&&
3416 vs
.vs_stack
[i
] < haddr
)
3423 if (thread
!= NULL
&& (uintptr_t)vs
.vs_thread
!= thread
)
3426 if (earliest
!= 0 && vs
.vs_timestamp
< earliest
)
3429 if (latest
!= 0 && vs
.vs_timestamp
> latest
)
3433 type
= (t
== VMEM_ALLOC
? "ALLC" :
3434 t
== VMEM_FREE
? "FREE" :
3435 t
== VMEM_SPAN
? "SPAN" :
3436 t
== VMEM_ROTOR
? "ROTR" :
3437 t
== VMEM_WALKER
? "WLKR" :
3440 if (flags
& DCMD_PIPE_OUT
) {
3441 mdb_printf("%#r\n", addr
);
3446 mdb_printf("%<b>%16p%</b> %4s %16p %16p %16d\n",
3447 addr
, type
, vs
.vs_start
, vs
.vs_end
, sz
);
3452 mdb_printf("%16s %4s %16d %16llx\n",
3453 "", "", vs
.vs_thread
, vs
.vs_timestamp
);
3456 for (i
= 0; i
< depth
; i
++) {
3457 mdb_printf("%a\n", stk
[i
]);
3462 mdb_printf("%0?p %4s %0?p %0?p", addr
, type
,
3463 vs
.vs_start
, size
? sz
: vs
.vs_end
);
3470 for (i
= 0; i
< depth
; i
++) {
3471 if (mdb_lookup_by_addr(stk
[i
], MDB_SYM_FUZZY
,
3472 c
, sizeof (c
), &sym
) == -1)
3474 if (is_umem_sym(c
, "vmem_"))
3478 mdb_printf(" %a\n", stk
[i
]);
3485 showbc(uintptr_t addr
, const umem_bufctl_audit_t
*bcp
, hrtime_t
*newest
)
3487 char name
[UMEM_CACHE_NAMELEN
+ 1];
3491 if (bcp
->bc_timestamp
== 0)
3495 *newest
= bcp
->bc_timestamp
;
3497 delta
= *newest
- bcp
->bc_timestamp
;
3498 depth
= MIN(bcp
->bc_depth
, umem_stack_depth
);
3500 if (mdb_readstr(name
, sizeof (name
), (uintptr_t)
3501 &bcp
->bc_cache
->cache_name
) <= 0)
3502 (void) mdb_snprintf(name
, sizeof (name
), "%a", bcp
->bc_cache
);
3504 mdb_printf("\nT-%lld.%09lld addr=%p %s\n",
3505 delta
/ NANOSEC
, delta
% NANOSEC
, bcp
->bc_addr
, name
);
3507 for (i
= 0; i
< depth
; i
++)
3508 mdb_printf("\t %a\n", bcp
->bc_stack
[i
]);
3514 umalog(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
3516 const char *logname
= "umem_transaction_log";
3517 hrtime_t newest
= 0;
3519 if ((flags
& DCMD_ADDRSPEC
) || argc
> 1)
3520 return (DCMD_USAGE
);
3523 if (argv
->a_type
!= MDB_TYPE_STRING
)
3524 return (DCMD_USAGE
);
3525 if (strcmp(argv
->a_un
.a_str
, "fail") == 0)
3526 logname
= "umem_failure_log";
3527 else if (strcmp(argv
->a_un
.a_str
, "slab") == 0)
3528 logname
= "umem_slab_log";
3530 return (DCMD_USAGE
);
3533 if (umem_readvar(&addr
, logname
) == -1) {
3534 mdb_warn("failed to read %s log header pointer");
3538 if (mdb_pwalk("umem_log", (mdb_walk_cb_t
)showbc
, &newest
, addr
) == -1) {
3539 mdb_warn("failed to walk umem log");
3547 * As the final lure for die-hard crash(1M) users, we provide ::umausers here.
3548 * The first piece is a structure which we use to accumulate umem_cache_t
3549 * addresses of interest. The umc_add is used as a callback for the umem_cache
3550 * walker; we either add all caches, or ones named explicitly as arguments.
3553 typedef struct umclist
{
3554 const char *umc_name
; /* Name to match (or NULL) */
3555 uintptr_t *umc_caches
; /* List of umem_cache_t addrs */
3556 int umc_nelems
; /* Num entries in umc_caches */
3557 int umc_size
; /* Size of umc_caches array */
3561 umc_add(uintptr_t addr
, const umem_cache_t
*cp
, umclist_t
*umc
)
3566 if (umc
->umc_name
== NULL
||
3567 strcmp(cp
->cache_name
, umc
->umc_name
) == 0) {
3569 * If we have a match, grow our array (if necessary), and then
3570 * add the virtual address of the matching cache to our list.
3572 if (umc
->umc_nelems
>= umc
->umc_size
) {
3573 s
= umc
->umc_size
? umc
->umc_size
* 2 : 256;
3574 p
= mdb_alloc(sizeof (uintptr_t) * s
, UM_SLEEP
| UM_GC
);
3576 bcopy(umc
->umc_caches
, p
,
3577 sizeof (uintptr_t) * umc
->umc_size
);
3579 umc
->umc_caches
= p
;
3583 umc
->umc_caches
[umc
->umc_nelems
++] = addr
;
3584 return (umc
->umc_name
? WALK_DONE
: WALK_NEXT
);
3591 * The second piece of ::umausers is a hash table of allocations. Each
3592 * allocation owner is identified by its stack trace and data_size. We then
3593 * track the total bytes of all such allocations, and the number of allocations
3594 * to report at the end. Once we have a list of caches, we walk through the
3595 * allocated bufctls of each, and update our hash table accordingly.
3598 typedef struct umowner
{
3599 struct umowner
*umo_head
; /* First hash elt in bucket */
3600 struct umowner
*umo_next
; /* Next hash elt in chain */
3601 size_t umo_signature
; /* Hash table signature */
3602 uint_t umo_num
; /* Number of allocations */
3603 size_t umo_data_size
; /* Size of each allocation */
3604 size_t umo_total_size
; /* Total bytes of allocation */
3605 int umo_depth
; /* Depth of stack trace */
3606 uintptr_t *umo_stack
; /* Stack trace */
3609 typedef struct umusers
{
3610 const umem_cache_t
*umu_cache
; /* Current umem cache */
3611 umowner_t
*umu_hash
; /* Hash table of owners */
3612 uintptr_t *umu_stacks
; /* stacks for owners */
3613 int umu_nelems
; /* Number of entries in use */
3614 int umu_size
; /* Total number of entries */
3618 umu_add(umusers_t
*umu
, const umem_bufctl_audit_t
*bcp
,
3619 size_t size
, size_t data_size
)
3621 int i
, depth
= MIN(bcp
->bc_depth
, umem_stack_depth
);
3622 size_t bucket
, signature
= data_size
;
3623 umowner_t
*umo
, *umoend
;
3626 * If the hash table is full, double its size and rehash everything.
3628 if (umu
->umu_nelems
>= umu
->umu_size
) {
3629 int s
= umu
->umu_size
? umu
->umu_size
* 2 : 1024;
3630 size_t umowner_size
= sizeof (umowner_t
);
3631 size_t trace_size
= umem_stack_depth
* sizeof (uintptr_t);
3632 uintptr_t *new_stacks
;
3634 umo
= mdb_alloc(umowner_size
* s
, UM_SLEEP
| UM_GC
);
3635 new_stacks
= mdb_alloc(trace_size
* s
, UM_SLEEP
| UM_GC
);
3637 bcopy(umu
->umu_hash
, umo
, umowner_size
* umu
->umu_size
);
3638 bcopy(umu
->umu_stacks
, new_stacks
, trace_size
* umu
->umu_size
);
3639 umu
->umu_hash
= umo
;
3640 umu
->umu_stacks
= new_stacks
;
3643 umoend
= umu
->umu_hash
+ umu
->umu_size
;
3644 for (umo
= umu
->umu_hash
; umo
< umoend
; umo
++) {
3645 umo
->umo_head
= NULL
;
3646 umo
->umo_stack
= &umu
->umu_stacks
[
3647 umem_stack_depth
* (umo
- umu
->umu_hash
)];
3650 umoend
= umu
->umu_hash
+ umu
->umu_nelems
;
3651 for (umo
= umu
->umu_hash
; umo
< umoend
; umo
++) {
3652 bucket
= umo
->umo_signature
& (umu
->umu_size
- 1);
3653 umo
->umo_next
= umu
->umu_hash
[bucket
].umo_head
;
3654 umu
->umu_hash
[bucket
].umo_head
= umo
;
3659 * Finish computing the hash signature from the stack trace, and then
3660 * see if the owner is in the hash table. If so, update our stats.
3662 for (i
= 0; i
< depth
; i
++)
3663 signature
+= bcp
->bc_stack
[i
];
3665 bucket
= signature
& (umu
->umu_size
- 1);
3667 for (umo
= umu
->umu_hash
[bucket
].umo_head
; umo
; umo
= umo
->umo_next
) {
3668 if (umo
->umo_signature
== signature
) {
3669 size_t difference
= 0;
3671 difference
|= umo
->umo_data_size
- data_size
;
3672 difference
|= umo
->umo_depth
- depth
;
3674 for (i
= 0; i
< depth
; i
++) {
3675 difference
|= umo
->umo_stack
[i
] -
3679 if (difference
== 0) {
3680 umo
->umo_total_size
+= size
;
3688 * If the owner is not yet hashed, grab the next element and fill it
3689 * in based on the allocation information.
3691 umo
= &umu
->umu_hash
[umu
->umu_nelems
++];
3692 umo
->umo_next
= umu
->umu_hash
[bucket
].umo_head
;
3693 umu
->umu_hash
[bucket
].umo_head
= umo
;
3695 umo
->umo_signature
= signature
;
3697 umo
->umo_data_size
= data_size
;
3698 umo
->umo_total_size
= size
;
3699 umo
->umo_depth
= depth
;
3701 for (i
= 0; i
< depth
; i
++)
3702 umo
->umo_stack
[i
] = bcp
->bc_stack
[i
];
3706 * When ::umausers is invoked without the -f flag, we simply update our hash
3707 * table with the information from each allocated bufctl.
3711 umause1(uintptr_t addr
, const umem_bufctl_audit_t
*bcp
, umusers_t
*umu
)
3713 const umem_cache_t
*cp
= umu
->umu_cache
;
3715 umu_add(umu
, bcp
, cp
->cache_bufsize
, cp
->cache_bufsize
);
3720 * When ::umausers is invoked with the -f flag, we print out the information
3721 * for each bufctl as well as updating the hash table.
3724 umause2(uintptr_t addr
, const umem_bufctl_audit_t
*bcp
, umusers_t
*umu
)
3726 int i
, depth
= MIN(bcp
->bc_depth
, umem_stack_depth
);
3727 const umem_cache_t
*cp
= umu
->umu_cache
;
3729 mdb_printf("size %d, addr %p, thread %p, cache %s\n",
3730 cp
->cache_bufsize
, addr
, bcp
->bc_thread
, cp
->cache_name
);
3732 for (i
= 0; i
< depth
; i
++)
3733 mdb_printf("\t %a\n", bcp
->bc_stack
[i
]);
3735 umu_add(umu
, bcp
, cp
->cache_bufsize
, cp
->cache_bufsize
);
3740 * We sort our results by allocation size before printing them.
3743 umownercmp(const void *lp
, const void *rp
)
3745 const umowner_t
*lhs
= lp
;
3746 const umowner_t
*rhs
= rp
;
3748 return (rhs
->umo_total_size
- lhs
->umo_total_size
);
3752 * The main engine of ::umausers is relatively straightforward: First we
3753 * accumulate our list of umem_cache_t addresses into the umclist_t. Next we
3754 * iterate over the allocated bufctls of each cache in the list. Finally,
3755 * we sort and print our results.
3759 umausers(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
3761 int mem_threshold
= 8192; /* Minimum # bytes for printing */
3762 int cnt_threshold
= 100; /* Minimum # blocks for printing */
3763 int audited_caches
= 0; /* Number of UMF_AUDIT caches found */
3764 int do_all_caches
= 1; /* Do all caches (no arguments) */
3765 int opt_e
= FALSE
; /* Include "small" users */
3766 int opt_f
= FALSE
; /* Print stack traces */
3768 mdb_walk_cb_t callback
= (mdb_walk_cb_t
)umause1
;
3769 umowner_t
*umo
, *umoend
;
3775 if (flags
& DCMD_ADDRSPEC
)
3776 return (DCMD_USAGE
);
3778 bzero(&umc
, sizeof (umc
));
3779 bzero(&umu
, sizeof (umu
));
3781 while ((i
= mdb_getopts(argc
, argv
,
3782 'e', MDB_OPT_SETBITS
, TRUE
, &opt_e
,
3783 'f', MDB_OPT_SETBITS
, TRUE
, &opt_f
, NULL
)) != argc
) {
3785 argv
+= i
; /* skip past options we just processed */
3786 argc
-= i
; /* adjust argc */
3788 if (argv
->a_type
!= MDB_TYPE_STRING
|| *argv
->a_un
.a_str
== '-')
3789 return (DCMD_USAGE
);
3791 oelems
= umc
.umc_nelems
;
3792 umc
.umc_name
= argv
->a_un
.a_str
;
3793 (void) mdb_walk("umem_cache", (mdb_walk_cb_t
)umc_add
, &umc
);
3795 if (umc
.umc_nelems
== oelems
) {
3796 mdb_warn("unknown umem cache: %s\n", umc
.umc_name
);
3806 mem_threshold
= cnt_threshold
= 0;
3809 callback
= (mdb_walk_cb_t
)umause2
;
3811 if (do_all_caches
) {
3812 umc
.umc_name
= NULL
; /* match all cache names */
3813 (void) mdb_walk("umem_cache", (mdb_walk_cb_t
)umc_add
, &umc
);
3816 for (i
= 0; i
< umc
.umc_nelems
; i
++) {
3817 uintptr_t cp
= umc
.umc_caches
[i
];
3820 if (mdb_vread(&c
, sizeof (c
), cp
) == -1) {
3821 mdb_warn("failed to read cache at %p", cp
);
3825 if (!(c
.cache_flags
& UMF_AUDIT
)) {
3826 if (!do_all_caches
) {
3827 mdb_warn("UMF_AUDIT is not enabled for %s\n",
3834 (void) mdb_pwalk("bufctl", callback
, &umu
, cp
);
3838 if (audited_caches
== 0 && do_all_caches
) {
3839 mdb_warn("UMF_AUDIT is not enabled for any caches\n");
3843 qsort(umu
.umu_hash
, umu
.umu_nelems
, sizeof (umowner_t
), umownercmp
);
3844 umoend
= umu
.umu_hash
+ umu
.umu_nelems
;
3846 for (umo
= umu
.umu_hash
; umo
< umoend
; umo
++) {
3847 if (umo
->umo_total_size
< mem_threshold
&&
3848 umo
->umo_num
< cnt_threshold
)
3850 mdb_printf("%lu bytes for %u allocations with data size %lu:\n",
3851 umo
->umo_total_size
, umo
->umo_num
, umo
->umo_data_size
);
3852 for (i
= 0; i
< umo
->umo_depth
; i
++)
3853 mdb_printf("\t %a\n", umo
->umo_stack
[i
]);
3859 struct malloc_data
{
3860 uint32_t malloc_size
;
3861 uint32_t malloc_stat
; /* == UMEM_MALLOC_ENCODE(state, malloc_size) */
3865 #define UMI_MAX_BUCKET (UMEM_MAXBUF - 2*sizeof (struct malloc_data))
3867 #define UMI_MAX_BUCKET (UMEM_MAXBUF - sizeof (struct malloc_data))
3870 typedef struct umem_malloc_info
{
3871 size_t um_total
; /* total allocated buffers */
3872 size_t um_malloc
; /* malloc buffers */
3873 size_t um_malloc_size
; /* sum of malloc buffer sizes */
3874 size_t um_malloc_overhead
; /* sum of in-chunk overheads */
3876 umem_cache_t
*um_cp
;
3879 } umem_malloc_info_t
;
3882 umem_malloc_print_dist(uint_t
*um_bucket
, size_t minmalloc
, size_t maxmalloc
,
3883 size_t maxbuckets
, size_t minbucketsize
, int geometric
)
3892 const int *distarray
;
3894 minb
= (int)minmalloc
;
3895 maxb
= (int)maxmalloc
;
3897 nbucks
= buckets
= maxb
- minb
+ 1;
3900 for (b
= minb
; b
<= maxb
; b
++)
3901 um_malloc
+= um_bucket
[b
];
3903 if (maxbuckets
!= 0)
3904 buckets
= MIN(buckets
, maxbuckets
);
3906 if (minbucketsize
> 1) {
3907 buckets
= MIN(buckets
, nbucks
/minbucketsize
);
3910 minbucketsize
= nbucks
;
3915 distarray
= dist_geometric(buckets
, minb
, maxb
, minbucketsize
);
3917 distarray
= dist_linear(buckets
, minb
, maxb
);
3919 dist_print_header("malloc size", 11, "count");
3920 for (i
= 0; i
< buckets
; i
++) {
3921 dist_print_bucket(distarray
, i
, um_bucket
, um_malloc
, 11);
3927 * A malloc()ed buffer looks like:
3929 * <----------- mi.malloc_size --->
3930 * <----------- cp.cache_bufsize ------------------>
3931 * <----------- cp.cache_chunksize -------------------------------->
3932 * +-------+-----------------------+---------------+---------------+
3933 * |/tag///| mallocsz |/round-off/////|/debug info////|
3934 * +-------+---------------------------------------+---------------+
3935 * <-- usable space ------>
3937 * mallocsz is the argument to malloc(3C).
3938 * mi.malloc_size is the actual size passed to umem_alloc(), which
3939 * is rounded up to the smallest available cache size, which is
3940 * cache_bufsize. If there is debugging or alignment overhead in
3941 * the cache, that is reflected in a larger cache_chunksize.
3943 * The tag at the beginning of the buffer is either 8-bytes or 16-bytes,
3944 * depending upon the ISA's alignment requirements. For 32-bit allocations,
3945 * it is always a 8-byte tag. For 64-bit allocations larger than 8 bytes,
3946 * the tag has 8 bytes of padding before it.
3948 * 32-byte, 64-byte buffers <= 8 bytes:
3949 * +-------+-------+--------- ...
3950 * |/size//|/stat//| mallocsz ...
3951 * +-------+-------+--------- ...
3953 * pointer returned from malloc(3C)
3955 * 64-byte buffers > 8 bytes:
3956 * +---------------+-------+-------+--------- ...
3957 * |/padding///////|/size//|/stat//| mallocsz ...
3958 * +---------------+-------+-------+--------- ...
3960 * pointer returned from malloc(3C)
3962 * The "size" field is "malloc_size", which is mallocsz + the padding.
3963 * The "stat" field is derived from malloc_size, and functions as a
3964 * validation that this buffer is actually from malloc(3C).
3968 um_umem_buffer_cb(uintptr_t addr
, void *buf
, umem_malloc_info_t
*ump
)
3970 struct malloc_data md
;
3971 size_t m_addr
= addr
;
3972 size_t overhead
= sizeof (md
);
3978 if (ump
->um_cp
->cache_bufsize
> UMEM_SECOND_ALIGN
) {
3980 overhead
+= sizeof (md
);
3984 if (mdb_vread(&md
, sizeof (md
), m_addr
) == -1) {
3985 mdb_warn("unable to read malloc header at %p", m_addr
);
3989 switch (UMEM_MALLOC_DECODE(md
.malloc_stat
, md
.malloc_size
)) {
3992 case MALLOC_SECOND_MAGIC
:
3994 mallocsz
= md
.malloc_size
- overhead
;
3997 ump
->um_malloc_size
+= mallocsz
;
3998 ump
->um_malloc_overhead
+= overhead
;
4000 /* include round-off and debug overhead */
4001 ump
->um_malloc_overhead
+=
4002 ump
->um_cp
->cache_chunksize
- md
.malloc_size
;
4004 if (ump
->um_bucket
!= NULL
&& mallocsz
<= UMI_MAX_BUCKET
)
4005 ump
->um_bucket
[mallocsz
]++;
4016 get_umem_alloc_sizes(int **out
, size_t *out_num
)
4020 if (umem_lookup_by_name("umem_alloc_sizes", &sym
) == -1) {
4021 mdb_warn("unable to look up umem_alloc_sizes");
4025 *out
= mdb_alloc(sym
.st_size
, UM_SLEEP
| UM_GC
);
4026 *out_num
= sym
.st_size
/ sizeof (int);
4028 if (mdb_vread(*out
, sym
.st_size
, sym
.st_value
) == -1) {
4029 mdb_warn("unable to read umem_alloc_sizes (%p)", sym
.st_value
);
4039 um_umem_cache_cb(uintptr_t addr
, umem_cache_t
*cp
, umem_malloc_info_t
*ump
)
4041 if (strncmp(cp
->cache_name
, "umem_alloc_", strlen("umem_alloc_")) != 0)
4046 if (mdb_pwalk("umem", (mdb_walk_cb_t
)um_umem_buffer_cb
, ump
, addr
) ==
4048 mdb_warn("can't walk 'umem' for cache %p", addr
);
4056 umem_malloc_dist_help(void)
4059 "report distribution of outstanding malloc()s");
4061 mdb_printf("%<b>OPTIONS%</b>\n");
4065 " Use at most maxbins bins for the data\n"
4067 " Make the bins at least minbinsize bytes apart\n"
4068 " -d dump the raw data out, without binning\n"
4069 " -g use geometric binning instead of linear binning\n");
4074 umem_malloc_dist(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
4076 umem_malloc_info_t mi
;
4077 uint_t geometric
= 0;
4079 size_t maxbuckets
= 0;
4080 size_t minbucketsize
= 0;
4082 size_t minalloc
= 0;
4083 size_t maxalloc
= UMI_MAX_BUCKET
;
4085 if (flags
& DCMD_ADDRSPEC
)
4086 return (DCMD_USAGE
);
4088 if (mdb_getopts(argc
, argv
,
4089 'd', MDB_OPT_SETBITS
, TRUE
, &dump
,
4090 'g', MDB_OPT_SETBITS
, TRUE
, &geometric
,
4091 'b', MDB_OPT_UINTPTR
, &maxbuckets
,
4092 'B', MDB_OPT_UINTPTR
, &minbucketsize
,
4094 return (DCMD_USAGE
);
4096 bzero(&mi
, sizeof (mi
));
4097 mi
.um_bucket
= mdb_zalloc((UMI_MAX_BUCKET
+ 1) * sizeof (*mi
.um_bucket
),
4100 if (mdb_walk("umem_cache", (mdb_walk_cb_t
)um_umem_cache_cb
,
4102 mdb_warn("unable to walk 'umem_cache'");
4108 for (i
= minalloc
; i
<= maxalloc
; i
++)
4109 mdb_printf("%d\t%d\n", i
, mi
.um_bucket
[i
]);
4114 umem_malloc_print_dist(mi
.um_bucket
, minalloc
, maxalloc
,
4115 maxbuckets
, minbucketsize
, geometric
);
4121 umem_malloc_info_help(void)
4124 "report information about malloc()s by cache. ");
4126 mdb_printf("%<b>OPTIONS%</b>\n");
4130 " Use at most maxbins bins for the data\n"
4132 " Make the bins at least minbinsize bytes apart\n"
4133 " -d dump the raw distribution data without binning\n"
4135 " -g use geometric binning instead of linear binning\n"
4140 umem_malloc_info(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
4143 umem_malloc_info_t mi
;
4151 size_t overhead_pct
; /* 1000 * overhead_percent */
4155 uint_t geometric
= 0;
4156 size_t maxbuckets
= 0;
4157 size_t minbucketsize
= 0;
4164 if (mdb_getopts(argc
, argv
,
4165 'd', MDB_OPT_SETBITS
, TRUE
, &dump
,
4166 'g', MDB_OPT_SETBITS
, TRUE
, &geometric
,
4167 'b', MDB_OPT_UINTPTR
, &maxbuckets
,
4168 'B', MDB_OPT_UINTPTR
, &minbucketsize
,
4170 return (DCMD_USAGE
);
4172 if (dump
|| geometric
|| (maxbuckets
!= 0) || (minbucketsize
!= 0))
4175 if (!(flags
& DCMD_ADDRSPEC
)) {
4176 if (mdb_walk_dcmd("umem_cache", "umem_malloc_info",
4177 argc
, argv
) == -1) {
4178 mdb_warn("can't walk umem_cache");
4184 if (!mdb_vread(&c
, sizeof (c
), addr
)) {
4185 mdb_warn("unable to read cache at %p", addr
);
4189 if (strncmp(c
.cache_name
, "umem_alloc_", strlen("umem_alloc_")) != 0) {
4190 if (!(flags
& DCMD_LOOP
))
4191 mdb_warn("umem_malloc_info: cache \"%s\" is not used "
4192 "by malloc()\n", c
.cache_name
);
4197 * normally, print the header only the first time. In verbose mode,
4198 * print the header on every non-skipped buffer
4200 if ((!verbose
&& DCMD_HDRSPEC(flags
)) || (verbose
&& !skip
))
4201 mdb_printf("%<ul>%-?s %6s %6s %8s %8s %10s %10s %6s%</ul>\n",
4202 "CACHE", "BUFSZ", "MAXMAL",
4203 "BUFMALLC", "AVG_MAL", "MALLOCED", "OVERHEAD", "%OVER");
4208 maxmalloc
= c
.cache_bufsize
- sizeof (struct malloc_data
);
4210 if (c
.cache_bufsize
> UMEM_SECOND_ALIGN
)
4211 maxmalloc
-= sizeof (struct malloc_data
);
4214 bzero(&mi
, sizeof (mi
));
4218 mdb_zalloc((UMI_MAX_BUCKET
+ 1) * sizeof (*mi
.um_bucket
),
4221 if (mdb_pwalk("umem", (mdb_walk_cb_t
)um_umem_buffer_cb
, &mi
, addr
) ==
4223 mdb_warn("can't walk 'umem'");
4227 overhead
= mi
.um_malloc_overhead
;
4228 allocated
= mi
.um_malloc_size
;
4230 /* do integer round off for the average */
4231 if (mi
.um_malloc
!= 0)
4232 avg_malloc
= (allocated
+ (mi
.um_malloc
- 1)/2) / mi
.um_malloc
;
4237 * include per-slab overhead
4239 * Each slab in a given cache is the same size, and has the same
4240 * number of chunks in it; we read in the first slab on the
4241 * slab list to get the number of chunks for all slabs. To
4242 * compute the per-slab overhead, we just subtract the chunk usage
4243 * from the slabsize:
4245 * +------------+-------+-------+ ... --+-------+-------+-------+
4246 * |////////////| | | ... | |///////|///////|
4247 * |////color///| chunk | chunk | ... | chunk |/color/|/slab//|
4248 * |////////////| | | ... | |///////|///////|
4249 * +------------+-------+-------+ ... --+-------+-------+-------+
4250 * | \_______chunksize * chunks_____/ |
4251 * \__________________________slabsize__________________________/
4253 * For UMF_HASH caches, there is an additional source of overhead;
4254 * the external umem_slab_t and per-chunk bufctl structures. We
4255 * include those in our per-slab overhead.
4257 * Once we have a number for the per-slab overhead, we estimate
4258 * the actual overhead by treating the malloc()ed buffers as if
4259 * they were densely packed:
4261 * additional overhead = (# mallocs) * (per-slab) / (chunks);
4263 * carefully ordering the multiply before the divide, to avoid
4266 if (mi
.um_malloc
!= 0) {
4268 uintptr_t saddr
= (uintptr_t)c
.cache_nullslab
.slab_next
;
4270 if (mdb_vread(&slab
, sizeof (slab
), saddr
) == -1) {
4271 mdb_warn("unable to read slab at %p\n", saddr
);
4273 long chunks
= slab
.slab_chunks
;
4274 if (chunks
!= 0 && c
.cache_chunksize
!= 0 &&
4275 chunks
<= c
.cache_slabsize
/ c
.cache_chunksize
) {
4278 (c
.cache_chunksize
* chunks
);
4280 if (c
.cache_flags
& UMF_HASH
) {
4281 perslab
+= sizeof (umem_slab_t
) +
4283 ((c
.cache_flags
& UMF_AUDIT
) ?
4284 sizeof (umem_bufctl_audit_t
) :
4285 sizeof (umem_bufctl_t
));
4288 (perslab
* (uintmax_t)mi
.um_malloc
)/chunks
;
4290 mdb_warn("invalid #chunks (%d) in slab %p\n",
4297 overhead_pct
= (1000ULL * overhead
) / allocated
;
4301 mdb_printf("%0?p %6ld %6ld %8ld %8ld %10ld %10ld %3ld.%01ld%%\n",
4302 addr
, c
.cache_bufsize
, maxmalloc
,
4303 mi
.um_malloc
, avg_malloc
, allocated
, overhead
,
4304 overhead_pct
/ 10, overhead_pct
% 10);
4312 if (get_umem_alloc_sizes(&alloc_sizes
, &num
) == -1)
4315 for (idx
= 0; idx
< num
; idx
++) {
4316 if (alloc_sizes
[idx
] == c
.cache_bufsize
)
4318 if (alloc_sizes
[idx
] == 0) {
4319 idx
= num
; /* 0-terminated array */
4325 "cache %p's size (%d) not in umem_alloc_sizes\n",
4326 addr
, c
.cache_bufsize
);
4330 minmalloc
= (idx
== 0)? 0 : alloc_sizes
[idx
- 1];
4331 if (minmalloc
> 0) {
4333 if (minmalloc
> UMEM_SECOND_ALIGN
)
4334 minmalloc
-= sizeof (struct malloc_data
);
4336 minmalloc
-= sizeof (struct malloc_data
);
4341 for (idx
= minmalloc
; idx
<= maxmalloc
; idx
++)
4342 mdb_printf("%d\t%d\n", idx
, mi
.um_bucket
[idx
]);
4345 umem_malloc_print_dist(mi
.um_bucket
, minmalloc
, maxmalloc
,
4346 maxbuckets
, minbucketsize
, geometric
);