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
== (uintptr_t)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);
140 if (this == (uintptr_t)NULL
)
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
;
306 if (pos
== (uintptr_t)NULL
)
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
== (uintptr_t)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
;
557 if (caddr
== (uintptr_t)NULL
) {
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
;
580 if (caddr
== (uintptr_t)NULL
) {
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
;
708 if (addr
== (uintptr_t)NULL
) {
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
= (uintptr_t)NULL
;
750 if ((addr
= (uintptr_t)umhw
->umhw_cur
.bc_next
) == (uintptr_t)NULL
) {
751 while (umhw
->umhw_pos
< umhw
->umhw_nelems
) {
752 if ((addr
= umhw
->umhw_table
[umhw
->umhw_pos
++]) !=
757 if (addr
== (uintptr_t)NULL
)
760 if (mdb_vread(&umhw
->umhw_cur
, sizeof (umem_bufctl_t
), addr
) == -1) {
761 mdb_warn("couldn't read umem_bufctl_t at addr %p", addr
);
765 return (wsp
->walk_callback(addr
, &umhw
->umhw_cur
, wsp
->walk_cbdata
));
769 umem_hash_walk_fini(mdb_walk_state_t
*wsp
)
771 umem_hash_walk_t
*umhw
= wsp
->walk_data
;
776 mdb_free(umhw
->umhw_table
, umhw
->umhw_nelems
* sizeof (uintptr_t));
777 mdb_free(umhw
, sizeof (umem_hash_walk_t
));
781 * Find the address of the bufctl structure for the address 'buf' in cache
782 * 'cp', which is at address caddr, and place it in *out.
785 umem_hash_lookup(umem_cache_t
*cp
, uintptr_t caddr
, void *buf
, uintptr_t *out
)
787 uintptr_t bucket
= (uintptr_t)UMEM_HASH(cp
, buf
);
791 if (mdb_vread(&bcp
, sizeof (umem_bufctl_t
*), bucket
) == -1) {
792 mdb_warn("unable to read hash bucket for %p in cache %p",
797 while (bcp
!= NULL
) {
798 if (mdb_vread(&bc
, sizeof (umem_bufctl_t
),
799 (uintptr_t)bcp
) == -1) {
800 mdb_warn("unable to read bufctl at %p", bcp
);
803 if (bc
.bc_addr
== buf
) {
804 *out
= (uintptr_t)bcp
;
810 mdb_warn("unable to find bufctl for %p in cache %p\n", buf
, caddr
);
815 umem_get_magsize(const umem_cache_t
*cp
)
817 uintptr_t addr
= (uintptr_t)cp
->cache_magtype
;
823 * if cpu 0 has a non-zero magsize, it must be correct. caches
824 * with UMF_NOMAGAZINE have disabled their magazine layers, so
825 * it is okay to return 0 for them.
827 if ((res
= cp
->cache_cpu
[0].cc_magsize
) != 0 ||
828 (cp
->cache_flags
& UMF_NOMAGAZINE
))
831 if (umem_lookup_by_name("umem_magtype", &mt_sym
) == -1) {
832 mdb_warn("unable to read 'umem_magtype'");
833 } else if (addr
< mt_sym
.st_value
||
834 addr
+ sizeof (mt
) - 1 > mt_sym
.st_value
+ mt_sym
.st_size
- 1 ||
835 ((addr
- mt_sym
.st_value
) % sizeof (mt
)) != 0) {
836 mdb_warn("cache '%s' has invalid magtype pointer (%p)\n",
837 cp
->cache_name
, addr
);
840 if (mdb_vread(&mt
, sizeof (mt
), addr
) == -1) {
841 mdb_warn("unable to read magtype at %a", addr
);
844 return (mt
.mt_magsize
);
849 umem_estimate_slab(uintptr_t addr
, const umem_slab_t
*sp
, size_t *est
)
851 *est
-= (sp
->slab_chunks
- sp
->slab_refcnt
);
857 * Returns an upper bound on the number of allocated buffers in a given
861 umem_estimate_allocated(uintptr_t addr
, const umem_cache_t
*cp
)
866 cache_est
= cp
->cache_buftotal
;
868 (void) mdb_pwalk("umem_slab_partial",
869 (mdb_walk_cb_t
)umem_estimate_slab
, &cache_est
, addr
);
871 if ((magsize
= umem_get_magsize(cp
)) != 0) {
872 size_t mag_est
= cp
->cache_full
.ml_total
* magsize
;
874 if (cache_est
>= mag_est
) {
875 cache_est
-= mag_est
;
877 mdb_warn("cache %p's magazine layer holds more buffers "
878 "than the slab layer.\n", addr
);
884 #define READMAG_ROUNDS(rounds) { \
885 if (mdb_vread(mp, magbsize, (uintptr_t)ump) == -1) { \
886 mdb_warn("couldn't read magazine at %p", ump); \
889 for (i = 0; i < rounds; i++) { \
890 maglist[magcnt++] = mp->mag_round[i]; \
891 if (magcnt == magmax) { \
892 mdb_warn("%d magazines exceeds fudge factor\n", \
900 umem_read_magazines(umem_cache_t
*cp
, uintptr_t addr
,
901 void ***maglistp
, size_t *magcntp
, size_t *magmaxp
)
903 umem_magazine_t
*ump
, *mp
;
904 void **maglist
= NULL
;
906 size_t magsize
, magmax
, magbsize
;
910 * Read the magtype out of the cache, after verifying the pointer's
913 magsize
= umem_get_magsize(cp
);
922 * There are several places where we need to go buffer hunting:
923 * the per-CPU loaded magazine, the per-CPU spare full magazine,
924 * and the full magazine list in the depot.
926 * For an upper bound on the number of buffers in the magazine
927 * layer, we have the number of magazines on the cache_full
928 * list plus at most two magazines per CPU (the loaded and the
929 * spare). Toss in 100 magazines as a fudge factor in case this
930 * is live (the number "100" comes from the same fudge factor in
933 magmax
= (cp
->cache_full
.ml_total
+ 2 * umem_max_ncpus
+ 100) * magsize
;
934 magbsize
= offsetof(umem_magazine_t
, mag_round
[magsize
]);
936 if (magbsize
>= PAGESIZE
/ 2) {
937 mdb_warn("magazine size for cache %p unreasonable (%x)\n",
942 maglist
= mdb_alloc(magmax
* sizeof (void *), UM_SLEEP
);
943 mp
= mdb_alloc(magbsize
, UM_SLEEP
);
944 if (mp
== NULL
|| maglist
== NULL
)
948 * First up: the magazines in the depot (i.e. on the cache_full list).
950 for (ump
= cp
->cache_full
.ml_list
; ump
!= NULL
; ) {
951 READMAG_ROUNDS(magsize
);
954 if (ump
== cp
->cache_full
.ml_list
)
955 break; /* cache_full list loop detected */
958 dprintf(("cache_full list done\n"));
961 * Now whip through the CPUs, snagging the loaded magazines
964 for (cpu
= 0; cpu
< umem_max_ncpus
; cpu
++) {
965 umem_cpu_cache_t
*ccp
= &cp
->cache_cpu
[cpu
];
967 dprintf(("reading cpu cache %p\n",
968 (uintptr_t)ccp
- (uintptr_t)cp
+ addr
));
970 if (ccp
->cc_rounds
> 0 &&
971 (ump
= ccp
->cc_loaded
) != NULL
) {
972 dprintf(("reading %d loaded rounds\n", ccp
->cc_rounds
));
973 READMAG_ROUNDS(ccp
->cc_rounds
);
976 if (ccp
->cc_prounds
> 0 &&
977 (ump
= ccp
->cc_ploaded
) != NULL
) {
978 dprintf(("reading %d previously loaded rounds\n",
980 READMAG_ROUNDS(ccp
->cc_prounds
);
984 dprintf(("magazine layer: %d buffers\n", magcnt
));
986 mdb_free(mp
, magbsize
);
996 mdb_free(mp
, magbsize
);
998 mdb_free(maglist
, magmax
* sizeof (void *));
1003 typedef struct umem_read_ptc_walk
{
1007 } umem_read_ptc_walk_t
;
1011 umem_read_ptc_walk_buf(uintptr_t addr
,
1012 const void *ignored
, umem_read_ptc_walk_t
*urpw
)
1014 if (urpw
->urpw_cnt
== urpw
->urpw_max
) {
1015 size_t nmax
= urpw
->urpw_max
? (urpw
->urpw_max
<< 1) : 1;
1016 void **new = mdb_zalloc(nmax
* sizeof (void *), UM_SLEEP
);
1019 size_t osize
= urpw
->urpw_max
* sizeof (void *);
1020 bcopy(urpw
->urpw_buf
, new, osize
);
1021 mdb_free(urpw
->urpw_buf
, osize
);
1024 urpw
->urpw_buf
= new;
1025 urpw
->urpw_max
= nmax
;
1028 urpw
->urpw_buf
[urpw
->urpw_cnt
++] = (void *)addr
;
1034 umem_read_ptc(umem_cache_t
*cp
,
1035 void ***buflistp
, size_t *bufcntp
, size_t *bufmaxp
)
1037 umem_read_ptc_walk_t urpw
;
1041 if (!(cp
->cache_flags
& UMF_PTC
))
1044 (void) mdb_snprintf(walk
, sizeof (walk
), "umem_ptc_%d",
1047 urpw
.urpw_buf
= *buflistp
;
1048 urpw
.urpw_cnt
= *bufcntp
;
1049 urpw
.urpw_max
= *bufmaxp
;
1051 if ((rval
= mdb_walk(walk
,
1052 (mdb_walk_cb_t
)umem_read_ptc_walk_buf
, &urpw
)) == -1) {
1053 mdb_warn("couldn't walk %s", walk
);
1056 *buflistp
= urpw
.urpw_buf
;
1057 *bufcntp
= urpw
.urpw_cnt
;
1058 *bufmaxp
= urpw
.urpw_max
;
1064 umem_walk_callback(mdb_walk_state_t
*wsp
, uintptr_t buf
)
1066 return (wsp
->walk_callback(buf
, NULL
, wsp
->walk_cbdata
));
1070 bufctl_walk_callback(umem_cache_t
*cp
, mdb_walk_state_t
*wsp
, uintptr_t buf
)
1072 umem_bufctl_audit_t
*b
;
1073 UMEM_LOCAL_BUFCTL_AUDIT(&b
);
1076 * if UMF_AUDIT is not set, we know that we're looking at a
1079 if (!(cp
->cache_flags
& UMF_AUDIT
) ||
1080 mdb_vread(b
, UMEM_BUFCTL_AUDIT_SIZE
, buf
) == -1) {
1081 (void) memset(b
, 0, UMEM_BUFCTL_AUDIT_SIZE
);
1082 if (mdb_vread(b
, sizeof (umem_bufctl_t
), buf
) == -1) {
1083 mdb_warn("unable to read bufctl at %p", buf
);
1088 return (wsp
->walk_callback(buf
, b
, wsp
->walk_cbdata
));
1091 typedef struct umem_walk
{
1094 uintptr_t umw_addr
; /* cache address */
1095 umem_cache_t
*umw_cp
;
1109 char *umw_valid
; /* to keep track of freed buffers */
1110 char *umw_ubase
; /* buffer for slab data */
1114 umem_walk_init_common(mdb_walk_state_t
*wsp
, int type
)
1121 size_t magmax
, magcnt
;
1122 void **maglist
= NULL
;
1123 uint_t chunksize
, slabsize
;
1124 int status
= WALK_ERR
;
1125 uintptr_t addr
= wsp
->walk_addr
;
1126 const char *layered
;
1130 if (addr
== (uintptr_t)NULL
) {
1131 mdb_warn("umem walk doesn't support global walks\n");
1135 dprintf(("walking %p\n", addr
));
1138 * The number of "cpus" determines how large the cache is.
1140 csize
= UMEM_CACHE_SIZE(umem_max_ncpus
);
1141 cp
= mdb_alloc(csize
, UM_SLEEP
);
1143 if (mdb_vread(cp
, csize
, addr
) == -1) {
1144 mdb_warn("couldn't read cache at addr %p", addr
);
1149 * It's easy for someone to hand us an invalid cache address.
1150 * Unfortunately, it is hard for this walker to survive an
1151 * invalid cache cleanly. So we make sure that:
1153 * 1. the vmem arena for the cache is readable,
1154 * 2. the vmem arena's quantum is a power of 2,
1155 * 3. our slabsize is a multiple of the quantum, and
1156 * 4. our chunksize is >0 and less than our slabsize.
1158 if (mdb_vread(&vm_quantum
, sizeof (vm_quantum
),
1159 (uintptr_t)&cp
->cache_arena
->vm_quantum
) == -1 ||
1161 (vm_quantum
& (vm_quantum
- 1)) != 0 ||
1162 cp
->cache_slabsize
< vm_quantum
||
1163 P2PHASE(cp
->cache_slabsize
, vm_quantum
) != 0 ||
1164 cp
->cache_chunksize
== 0 ||
1165 cp
->cache_chunksize
> cp
->cache_slabsize
) {
1166 mdb_warn("%p is not a valid umem_cache_t\n", addr
);
1170 dprintf(("buf total is %d\n", cp
->cache_buftotal
));
1172 if (cp
->cache_buftotal
== 0) {
1173 mdb_free(cp
, csize
);
1178 * If they ask for bufctls, but it's a small-slab cache,
1179 * there is nothing to report.
1181 if ((type
& UM_BUFCTL
) && !(cp
->cache_flags
& UMF_HASH
)) {
1182 dprintf(("bufctl requested, not UMF_HASH (flags: %p)\n",
1184 mdb_free(cp
, csize
);
1189 * Read in the contents of the magazine layer
1191 if (umem_read_magazines(cp
, addr
, &maglist
, &magcnt
, &magmax
) != 0)
1195 * Read in the contents of the per-thread caches, if any
1197 if (umem_read_ptc(cp
, &maglist
, &magcnt
, &magmax
) != 0)
1201 * We have all of the buffers from the magazines and from the
1202 * per-thread cache (if any); if we are walking allocated buffers,
1203 * sort them so we can bsearch them later.
1205 if (type
& UM_ALLOCATED
)
1206 qsort(maglist
, magcnt
, sizeof (void *), addrcmp
);
1208 wsp
->walk_data
= umw
= mdb_zalloc(sizeof (umem_walk_t
), UM_SLEEP
);
1210 umw
->umw_type
= type
;
1211 umw
->umw_addr
= addr
;
1213 umw
->umw_csize
= csize
;
1214 umw
->umw_maglist
= maglist
;
1215 umw
->umw_max
= magmax
;
1216 umw
->umw_count
= magcnt
;
1220 * When walking allocated buffers in a UMF_HASH cache, we walk the
1221 * hash table instead of the slab layer.
1223 if ((cp
->cache_flags
& UMF_HASH
) && (type
& UM_ALLOCATED
)) {
1224 layered
= "umem_hash";
1226 umw
->umw_type
|= UM_HASH
;
1229 * If we are walking freed buffers, we only need the
1230 * magazine layer plus the partially allocated slabs.
1231 * To walk allocated buffers, we need all of the slabs.
1233 if (type
& UM_ALLOCATED
)
1234 layered
= "umem_slab";
1236 layered
= "umem_slab_partial";
1239 * for small-slab caches, we read in the entire slab. For
1240 * freed buffers, we can just walk the freelist. For
1241 * allocated buffers, we use a 'valid' array to track
1242 * the freed buffers.
1244 if (!(cp
->cache_flags
& UMF_HASH
)) {
1245 chunksize
= cp
->cache_chunksize
;
1246 slabsize
= cp
->cache_slabsize
;
1248 umw
->umw_ubase
= mdb_alloc(slabsize
+
1249 sizeof (umem_bufctl_t
), UM_SLEEP
);
1251 if (type
& UM_ALLOCATED
)
1253 mdb_alloc(slabsize
/ chunksize
, UM_SLEEP
);
1259 if (mdb_layered_walk(layered
, wsp
) == -1) {
1260 mdb_warn("unable to start layered '%s' walk", layered
);
1265 if (status
== WALK_ERR
) {
1267 mdb_free(umw
->umw_valid
, slabsize
/ chunksize
);
1270 mdb_free(umw
->umw_ubase
, slabsize
+
1271 sizeof (umem_bufctl_t
));
1273 if (umw
->umw_maglist
)
1274 mdb_free(umw
->umw_maglist
, umw
->umw_max
*
1275 sizeof (uintptr_t));
1277 mdb_free(umw
, sizeof (umem_walk_t
));
1278 wsp
->walk_data
= NULL
;
1282 if (status
== WALK_ERR
)
1283 mdb_free(cp
, csize
);
1289 umem_walk_step(mdb_walk_state_t
*wsp
)
1291 umem_walk_t
*umw
= wsp
->walk_data
;
1292 int type
= umw
->umw_type
;
1293 umem_cache_t
*cp
= umw
->umw_cp
;
1295 void **maglist
= umw
->umw_maglist
;
1296 int magcnt
= umw
->umw_count
;
1298 uintptr_t chunksize
, slabsize
;
1300 const umem_slab_t
*sp
;
1301 const umem_bufctl_t
*bcp
;
1309 char *valid
, *ubase
;
1312 * first, handle the 'umem_hash' layered walk case
1314 if (type
& UM_HASH
) {
1316 * We have a buffer which has been allocated out of the
1317 * global layer. We need to make sure that it's not
1318 * actually sitting in a magazine before we report it as
1319 * an allocated buffer.
1321 buf
= ((const umem_bufctl_t
*)wsp
->walk_layer
)->bc_addr
;
1324 bsearch(&buf
, maglist
, magcnt
, sizeof (void *),
1328 if (type
& UM_BUFCTL
)
1329 return (bufctl_walk_callback(cp
, wsp
, wsp
->walk_addr
));
1331 return (umem_walk_callback(wsp
, (uintptr_t)buf
));
1336 addr
= umw
->umw_addr
;
1339 * If we're walking freed buffers, report everything in the
1340 * magazine layer before processing the first slab.
1342 if ((type
& UM_FREE
) && magcnt
!= 0) {
1343 umw
->umw_count
= 0; /* only do this once */
1344 for (i
= 0; i
< magcnt
; i
++) {
1347 if (type
& UM_BUFCTL
) {
1350 if (cp
->cache_flags
& UMF_BUFTAG
) {
1354 /* LINTED - alignment */
1355 btp
= UMEM_BUFTAG(cp
, buf
);
1356 if (mdb_vread(&tag
, sizeof (tag
),
1357 (uintptr_t)btp
) == -1) {
1358 mdb_warn("reading buftag for "
1359 "%p at %p", buf
, btp
);
1362 out
= (uintptr_t)tag
.bt_bufctl
;
1364 if (umem_hash_lookup(cp
, addr
, buf
,
1368 ret
= bufctl_walk_callback(cp
, wsp
, out
);
1370 ret
= umem_walk_callback(wsp
, (uintptr_t)buf
);
1373 if (ret
!= WALK_NEXT
)
1379 * Handle the buffers in the current slab
1381 chunksize
= cp
->cache_chunksize
;
1382 slabsize
= cp
->cache_slabsize
;
1384 sp
= wsp
->walk_layer
;
1385 chunks
= sp
->slab_chunks
;
1386 kbase
= sp
->slab_base
;
1388 dprintf(("kbase is %p\n", kbase
));
1390 if (!(cp
->cache_flags
& UMF_HASH
)) {
1391 valid
= umw
->umw_valid
;
1392 ubase
= umw
->umw_ubase
;
1394 if (mdb_vread(ubase
, chunks
* chunksize
,
1395 (uintptr_t)kbase
) == -1) {
1396 mdb_warn("failed to read slab contents at %p", kbase
);
1401 * Set up the valid map as fully allocated -- we'll punch
1404 if (type
& UM_ALLOCATED
)
1405 (void) memset(valid
, 1, chunks
);
1412 * walk the slab's freelist
1414 bcp
= sp
->slab_head
;
1416 dprintf(("refcnt is %d; chunks is %d\n", sp
->slab_refcnt
, chunks
));
1419 * since we could be in the middle of allocating a buffer,
1420 * our refcnt could be one higher than it aught. So we
1421 * check one further on the freelist than the count allows.
1423 for (i
= sp
->slab_refcnt
; i
<= chunks
; i
++) {
1426 dprintf(("bcp is %p\n", bcp
));
1432 "slab %p in cache %p freelist too short by %d\n",
1433 sp
, addr
, chunks
- i
);
1437 if (cp
->cache_flags
& UMF_HASH
) {
1438 if (mdb_vread(&bc
, sizeof (bc
), (uintptr_t)bcp
) == -1) {
1439 mdb_warn("failed to read bufctl ptr at %p",
1446 * Otherwise the buffer is (or should be) in the slab
1447 * that we've read in; determine its offset in the
1448 * slab, validate that it's not corrupt, and add to
1449 * our base address to find the umem_bufctl_t. (Note
1450 * that we don't need to add the size of the bufctl
1451 * to our offset calculation because of the slop that's
1452 * allocated for the buffer at ubase.)
1454 uintptr_t offs
= (uintptr_t)bcp
- (uintptr_t)kbase
;
1456 if (offs
> chunks
* chunksize
) {
1457 mdb_warn("found corrupt bufctl ptr %p"
1458 " in slab %p in cache %p\n", bcp
,
1459 wsp
->walk_addr
, addr
);
1463 bc
= *((umem_bufctl_t
*)((uintptr_t)ubase
+ offs
));
1464 buf
= UMEM_BUF(cp
, bcp
);
1467 ndx
= ((uintptr_t)buf
- (uintptr_t)kbase
) / chunksize
;
1469 if (ndx
> slabsize
/ cp
->cache_bufsize
) {
1471 * This is very wrong; we have managed to find
1472 * a buffer in the slab which shouldn't
1473 * actually be here. Emit a warning, and
1476 mdb_warn("buf %p is out of range for "
1477 "slab %p, cache %p\n", buf
, sp
, addr
);
1478 } else if (type
& UM_ALLOCATED
) {
1480 * we have found a buffer on the slab's freelist;
1486 * Report this freed buffer
1488 if (type
& UM_BUFCTL
) {
1489 ret
= bufctl_walk_callback(cp
, wsp
,
1492 ret
= umem_walk_callback(wsp
, (uintptr_t)buf
);
1494 if (ret
!= WALK_NEXT
)
1502 dprintf(("slab %p in cache %p freelist too long (%p)\n",
1507 * If we are walking freed buffers, the loop above handled reporting
1513 if (type
& UM_BUFCTL
) {
1514 mdb_warn("impossible situation: small-slab UM_BUFCTL walk for "
1515 "cache %p\n", addr
);
1520 * Report allocated buffers, skipping buffers in the magazine layer.
1521 * We only get this far for small-slab caches.
1523 for (i
= 0; ret
== WALK_NEXT
&& i
< chunks
; i
++) {
1524 buf
= (char *)kbase
+ i
* chunksize
;
1527 continue; /* on slab freelist */
1530 bsearch(&buf
, maglist
, magcnt
, sizeof (void *),
1532 continue; /* in magazine layer */
1534 ret
= umem_walk_callback(wsp
, (uintptr_t)buf
);
1540 umem_walk_fini(mdb_walk_state_t
*wsp
)
1542 umem_walk_t
*umw
= wsp
->walk_data
;
1543 uintptr_t chunksize
;
1549 if (umw
->umw_maglist
!= NULL
)
1550 mdb_free(umw
->umw_maglist
, umw
->umw_max
* sizeof (void *));
1552 chunksize
= umw
->umw_cp
->cache_chunksize
;
1553 slabsize
= umw
->umw_cp
->cache_slabsize
;
1555 if (umw
->umw_valid
!= NULL
)
1556 mdb_free(umw
->umw_valid
, slabsize
/ chunksize
);
1557 if (umw
->umw_ubase
!= NULL
)
1558 mdb_free(umw
->umw_ubase
, slabsize
+ sizeof (umem_bufctl_t
));
1560 mdb_free(umw
->umw_cp
, umw
->umw_csize
);
1561 mdb_free(umw
, sizeof (umem_walk_t
));
1566 umem_walk_all(uintptr_t addr
, const umem_cache_t
*c
, mdb_walk_state_t
*wsp
)
1569 * Buffers allocated from NOTOUCH caches can also show up as freed
1570 * memory in other caches. This can be a little confusing, so we
1571 * don't walk NOTOUCH caches when walking all caches (thereby assuring
1572 * that "::walk umem" and "::walk freemem" yield disjoint output).
1574 if (c
->cache_cflags
& UMC_NOTOUCH
)
1577 if (mdb_pwalk(wsp
->walk_data
, wsp
->walk_callback
,
1578 wsp
->walk_cbdata
, addr
) == -1)
1584 #define UMEM_WALK_ALL(name, wsp) { \
1585 wsp->walk_data = (name); \
1586 if (mdb_walk("umem_cache", (mdb_walk_cb_t)umem_walk_all, wsp) == -1) \
1587 return (WALK_ERR); \
1588 return (WALK_DONE); \
1592 umem_walk_init(mdb_walk_state_t
*wsp
)
1594 if (wsp
->walk_arg
!= NULL
)
1595 wsp
->walk_addr
= (uintptr_t)wsp
->walk_arg
;
1597 if (wsp
->walk_addr
== (uintptr_t)NULL
)
1598 UMEM_WALK_ALL("umem", wsp
);
1599 return (umem_walk_init_common(wsp
, UM_ALLOCATED
));
1603 bufctl_walk_init(mdb_walk_state_t
*wsp
)
1605 if (wsp
->walk_addr
== (uintptr_t)NULL
)
1606 UMEM_WALK_ALL("bufctl", wsp
);
1607 return (umem_walk_init_common(wsp
, UM_ALLOCATED
| UM_BUFCTL
));
1611 freemem_walk_init(mdb_walk_state_t
*wsp
)
1613 if (wsp
->walk_addr
== (uintptr_t)NULL
)
1614 UMEM_WALK_ALL("freemem", wsp
);
1615 return (umem_walk_init_common(wsp
, UM_FREE
));
1619 freectl_walk_init(mdb_walk_state_t
*wsp
)
1621 if (wsp
->walk_addr
== (uintptr_t)NULL
)
1622 UMEM_WALK_ALL("freectl", wsp
);
1623 return (umem_walk_init_common(wsp
, UM_FREE
| UM_BUFCTL
));
1626 typedef struct bufctl_history_walk
{
1628 umem_cache_t
*bhw_cache
;
1629 umem_slab_t
*bhw_slab
;
1630 hrtime_t bhw_timestamp
;
1631 } bufctl_history_walk_t
;
1634 bufctl_history_walk_init(mdb_walk_state_t
*wsp
)
1636 bufctl_history_walk_t
*bhw
;
1637 umem_bufctl_audit_t bc
;
1638 umem_bufctl_audit_t bcn
;
1640 if (wsp
->walk_addr
== (uintptr_t)NULL
) {
1641 mdb_warn("bufctl_history walk doesn't support global walks\n");
1645 if (mdb_vread(&bc
, sizeof (bc
), wsp
->walk_addr
) == -1) {
1646 mdb_warn("unable to read bufctl at %p", wsp
->walk_addr
);
1650 bhw
= mdb_zalloc(sizeof (*bhw
), UM_SLEEP
);
1651 bhw
->bhw_timestamp
= 0;
1652 bhw
->bhw_cache
= bc
.bc_cache
;
1653 bhw
->bhw_slab
= bc
.bc_slab
;
1656 * sometimes the first log entry matches the base bufctl; in that
1657 * case, skip the base bufctl.
1659 if (bc
.bc_lastlog
!= NULL
&&
1660 mdb_vread(&bcn
, sizeof (bcn
), (uintptr_t)bc
.bc_lastlog
) != -1 &&
1661 bc
.bc_addr
== bcn
.bc_addr
&&
1662 bc
.bc_cache
== bcn
.bc_cache
&&
1663 bc
.bc_slab
== bcn
.bc_slab
&&
1664 bc
.bc_timestamp
== bcn
.bc_timestamp
&&
1665 bc
.bc_thread
== bcn
.bc_thread
)
1666 bhw
->bhw_next
= bc
.bc_lastlog
;
1668 bhw
->bhw_next
= (void *)wsp
->walk_addr
;
1670 wsp
->walk_addr
= (uintptr_t)bc
.bc_addr
;
1671 wsp
->walk_data
= bhw
;
1677 bufctl_history_walk_step(mdb_walk_state_t
*wsp
)
1679 bufctl_history_walk_t
*bhw
= wsp
->walk_data
;
1680 uintptr_t addr
= (uintptr_t)bhw
->bhw_next
;
1681 uintptr_t baseaddr
= wsp
->walk_addr
;
1682 umem_bufctl_audit_t
*b
;
1683 UMEM_LOCAL_BUFCTL_AUDIT(&b
);
1685 if (addr
== (uintptr_t)NULL
)
1688 if (mdb_vread(b
, UMEM_BUFCTL_AUDIT_SIZE
, addr
) == -1) {
1689 mdb_warn("unable to read bufctl at %p", bhw
->bhw_next
);
1694 * The bufctl is only valid if the address, cache, and slab are
1695 * correct. We also check that the timestamp is decreasing, to
1696 * prevent infinite loops.
1698 if ((uintptr_t)b
->bc_addr
!= baseaddr
||
1699 b
->bc_cache
!= bhw
->bhw_cache
||
1700 b
->bc_slab
!= bhw
->bhw_slab
||
1701 (bhw
->bhw_timestamp
!= 0 && b
->bc_timestamp
>= bhw
->bhw_timestamp
))
1704 bhw
->bhw_next
= b
->bc_lastlog
;
1705 bhw
->bhw_timestamp
= b
->bc_timestamp
;
1707 return (wsp
->walk_callback(addr
, b
, wsp
->walk_cbdata
));
1711 bufctl_history_walk_fini(mdb_walk_state_t
*wsp
)
1713 bufctl_history_walk_t
*bhw
= wsp
->walk_data
;
1715 mdb_free(bhw
, sizeof (*bhw
));
1718 typedef struct umem_log_walk
{
1719 umem_bufctl_audit_t
*ulw_base
;
1720 umem_bufctl_audit_t
**ulw_sorted
;
1721 umem_log_header_t ulw_lh
;
1728 umem_log_walk_init(mdb_walk_state_t
*wsp
)
1730 uintptr_t lp
= wsp
->walk_addr
;
1731 umem_log_walk_t
*ulw
;
1732 umem_log_header_t
*lhp
;
1733 int maxndx
, i
, j
, k
;
1736 * By default (global walk), walk the umem_transaction_log. Otherwise
1737 * read the log whose umem_log_header_t is stored at walk_addr.
1739 if (lp
== (uintptr_t)NULL
&&
1740 umem_readvar(&lp
, "umem_transaction_log") == -1) {
1741 mdb_warn("failed to read 'umem_transaction_log'");
1745 if (lp
== (uintptr_t)NULL
) {
1746 mdb_warn("log is disabled\n");
1750 ulw
= mdb_zalloc(sizeof (umem_log_walk_t
), UM_SLEEP
);
1753 if (mdb_vread(lhp
, sizeof (umem_log_header_t
), lp
) == -1) {
1754 mdb_warn("failed to read log header at %p", lp
);
1755 mdb_free(ulw
, sizeof (umem_log_walk_t
));
1759 ulw
->ulw_size
= lhp
->lh_chunksize
* lhp
->lh_nchunks
;
1760 ulw
->ulw_base
= mdb_alloc(ulw
->ulw_size
, UM_SLEEP
);
1761 maxndx
= lhp
->lh_chunksize
/ UMEM_BUFCTL_AUDIT_SIZE
- 1;
1763 if (mdb_vread(ulw
->ulw_base
, ulw
->ulw_size
,
1764 (uintptr_t)lhp
->lh_base
) == -1) {
1765 mdb_warn("failed to read log at base %p", lhp
->lh_base
);
1766 mdb_free(ulw
->ulw_base
, ulw
->ulw_size
);
1767 mdb_free(ulw
, sizeof (umem_log_walk_t
));
1771 ulw
->ulw_sorted
= mdb_alloc(maxndx
* lhp
->lh_nchunks
*
1772 sizeof (umem_bufctl_audit_t
*), UM_SLEEP
);
1774 for (i
= 0, k
= 0; i
< lhp
->lh_nchunks
; i
++) {
1775 caddr_t chunk
= (caddr_t
)
1776 ((uintptr_t)ulw
->ulw_base
+ i
* lhp
->lh_chunksize
);
1778 for (j
= 0; j
< maxndx
; j
++) {
1780 ulw
->ulw_sorted
[k
++] = (umem_bufctl_audit_t
*)chunk
;
1781 chunk
+= UMEM_BUFCTL_AUDIT_SIZE
;
1785 qsort(ulw
->ulw_sorted
, k
, sizeof (umem_bufctl_audit_t
*),
1786 (int(*)(const void *, const void *))bufctlcmp
);
1788 ulw
->ulw_maxndx
= k
;
1789 wsp
->walk_data
= ulw
;
1795 umem_log_walk_step(mdb_walk_state_t
*wsp
)
1797 umem_log_walk_t
*ulw
= wsp
->walk_data
;
1798 umem_bufctl_audit_t
*bcp
;
1800 if (ulw
->ulw_ndx
== ulw
->ulw_maxndx
)
1803 bcp
= ulw
->ulw_sorted
[ulw
->ulw_ndx
++];
1805 return (wsp
->walk_callback((uintptr_t)bcp
- (uintptr_t)ulw
->ulw_base
+
1806 (uintptr_t)ulw
->ulw_lh
.lh_base
, bcp
, wsp
->walk_cbdata
));
1810 umem_log_walk_fini(mdb_walk_state_t
*wsp
)
1812 umem_log_walk_t
*ulw
= wsp
->walk_data
;
1814 mdb_free(ulw
->ulw_base
, ulw
->ulw_size
);
1815 mdb_free(ulw
->ulw_sorted
, ulw
->ulw_maxndx
*
1816 sizeof (umem_bufctl_audit_t
*));
1817 mdb_free(ulw
, sizeof (umem_log_walk_t
));
1820 typedef struct allocdby_bufctl
{
1823 } allocdby_bufctl_t
;
1825 typedef struct allocdby_walk
{
1826 const char *abw_walk
;
1827 uintptr_t abw_thread
;
1830 allocdby_bufctl_t
*abw_buf
;
1835 allocdby_walk_bufctl(uintptr_t addr
, const umem_bufctl_audit_t
*bcp
,
1836 allocdby_walk_t
*abw
)
1838 if ((uintptr_t)bcp
->bc_thread
!= abw
->abw_thread
)
1841 if (abw
->abw_nbufs
== abw
->abw_size
) {
1842 allocdby_bufctl_t
*buf
;
1843 size_t oldsize
= sizeof (allocdby_bufctl_t
) * abw
->abw_size
;
1845 buf
= mdb_zalloc(oldsize
<< 1, UM_SLEEP
);
1847 bcopy(abw
->abw_buf
, buf
, oldsize
);
1848 mdb_free(abw
->abw_buf
, oldsize
);
1850 abw
->abw_size
<<= 1;
1854 abw
->abw_buf
[abw
->abw_nbufs
].abb_addr
= addr
;
1855 abw
->abw_buf
[abw
->abw_nbufs
].abb_ts
= bcp
->bc_timestamp
;
1863 allocdby_walk_cache(uintptr_t addr
, const umem_cache_t
*c
, allocdby_walk_t
*abw
)
1865 if (mdb_pwalk(abw
->abw_walk
, (mdb_walk_cb_t
)allocdby_walk_bufctl
,
1867 mdb_warn("couldn't walk bufctl for cache %p", addr
);
1875 allocdby_cmp(const allocdby_bufctl_t
*lhs
, const allocdby_bufctl_t
*rhs
)
1877 if (lhs
->abb_ts
< rhs
->abb_ts
)
1879 if (lhs
->abb_ts
> rhs
->abb_ts
)
1885 allocdby_walk_init_common(mdb_walk_state_t
*wsp
, const char *walk
)
1887 allocdby_walk_t
*abw
;
1889 if (wsp
->walk_addr
== (uintptr_t)NULL
) {
1890 mdb_warn("allocdby walk doesn't support global walks\n");
1894 abw
= mdb_zalloc(sizeof (allocdby_walk_t
), UM_SLEEP
);
1896 abw
->abw_thread
= wsp
->walk_addr
;
1897 abw
->abw_walk
= walk
;
1898 abw
->abw_size
= 128; /* something reasonable */
1900 mdb_zalloc(abw
->abw_size
* sizeof (allocdby_bufctl_t
), UM_SLEEP
);
1902 wsp
->walk_data
= abw
;
1904 if (mdb_walk("umem_cache",
1905 (mdb_walk_cb_t
)allocdby_walk_cache
, abw
) == -1) {
1906 mdb_warn("couldn't walk umem_cache");
1907 allocdby_walk_fini(wsp
);
1911 qsort(abw
->abw_buf
, abw
->abw_nbufs
, sizeof (allocdby_bufctl_t
),
1912 (int(*)(const void *, const void *))allocdby_cmp
);
1918 allocdby_walk_init(mdb_walk_state_t
*wsp
)
1920 return (allocdby_walk_init_common(wsp
, "bufctl"));
1924 freedby_walk_init(mdb_walk_state_t
*wsp
)
1926 return (allocdby_walk_init_common(wsp
, "freectl"));
1930 allocdby_walk_step(mdb_walk_state_t
*wsp
)
1932 allocdby_walk_t
*abw
= wsp
->walk_data
;
1934 umem_bufctl_audit_t
*bcp
;
1935 UMEM_LOCAL_BUFCTL_AUDIT(&bcp
);
1937 if (abw
->abw_ndx
== abw
->abw_nbufs
)
1940 addr
= abw
->abw_buf
[abw
->abw_ndx
++].abb_addr
;
1942 if (mdb_vread(bcp
, UMEM_BUFCTL_AUDIT_SIZE
, addr
) == -1) {
1943 mdb_warn("couldn't read bufctl at %p", addr
);
1947 return (wsp
->walk_callback(addr
, bcp
, wsp
->walk_cbdata
));
1951 allocdby_walk_fini(mdb_walk_state_t
*wsp
)
1953 allocdby_walk_t
*abw
= wsp
->walk_data
;
1955 mdb_free(abw
->abw_buf
, sizeof (allocdby_bufctl_t
) * abw
->abw_size
);
1956 mdb_free(abw
, sizeof (allocdby_walk_t
));
1961 allocdby_walk(uintptr_t addr
, const umem_bufctl_audit_t
*bcp
, void *ignored
)
1963 char c
[MDB_SYM_NAMLEN
];
1967 mdb_printf("%0?p %12llx ", addr
, bcp
->bc_timestamp
);
1968 for (i
= 0; i
< bcp
->bc_depth
; i
++) {
1969 if (mdb_lookup_by_addr(bcp
->bc_stack
[i
],
1970 MDB_SYM_FUZZY
, c
, sizeof (c
), &sym
) == -1)
1972 if (is_umem_sym(c
, "umem_"))
1974 mdb_printf("%s+0x%lx",
1975 c
, bcp
->bc_stack
[i
] - (uintptr_t)sym
.st_value
);
1984 allocdby_common(uintptr_t addr
, uint_t flags
, const char *w
)
1986 if (!(flags
& DCMD_ADDRSPEC
))
1987 return (DCMD_USAGE
);
1989 mdb_printf("%-?s %12s %s\n", "BUFCTL", "TIMESTAMP", "CALLER");
1991 if (mdb_pwalk(w
, (mdb_walk_cb_t
)allocdby_walk
, NULL
, addr
) == -1) {
1992 mdb_warn("can't walk '%s' for %p", w
, addr
);
2001 allocdby(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2003 return (allocdby_common(addr
, flags
, "allocdby"));
2008 freedby(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2010 return (allocdby_common(addr
, flags
, "freedby"));
2013 typedef struct whatis_info
{
2015 const umem_cache_t
*wi_cache
;
2016 const vmem_t
*wi_vmem
;
2017 vmem_t
*wi_msb_arena
;
2018 size_t wi_slab_size
;
2023 /* call one of our dcmd functions with "-v" and the provided address */
2025 whatis_call_printer(mdb_dcmd_f
*dcmd
, uintptr_t addr
)
2028 a
.a_type
= MDB_TYPE_STRING
;
2029 a
.a_un
.a_str
= "-v";
2032 (void) (*dcmd
)(addr
, DCMD_ADDRSPEC
, 1, &a
);
2036 whatis_print_umem(whatis_info_t
*wi
, uintptr_t maddr
, uintptr_t addr
,
2039 mdb_whatis_t
*w
= wi
->wi_w
;
2040 const umem_cache_t
*cp
= wi
->wi_cache
;
2041 int quiet
= (mdb_whatis_flags(w
) & WHATIS_QUIET
);
2043 int call_printer
= (!quiet
&& (cp
->cache_flags
& UMF_AUDIT
));
2045 mdb_whatis_report_object(w
, maddr
, addr
, "");
2047 if (baddr
!= (uintptr_t)NULL
&& !call_printer
)
2048 mdb_printf("bufctl %p ", baddr
);
2050 mdb_printf("%s from %s",
2051 (wi
->wi_freemem
== FALSE
) ? "allocated" : "freed", cp
->cache_name
);
2053 if (call_printer
&& baddr
!= (uintptr_t)NULL
) {
2054 whatis_call_printer(bufctl
, baddr
);
2062 whatis_walk_umem(uintptr_t addr
, void *ignored
, whatis_info_t
*wi
)
2064 mdb_whatis_t
*w
= wi
->wi_w
;
2067 size_t size
= wi
->wi_cache
->cache_bufsize
;
2069 while (mdb_whatis_match(w
, addr
, size
, &cur
))
2070 whatis_print_umem(wi
, cur
, addr
, (uintptr_t)NULL
);
2072 return (WHATIS_WALKRET(w
));
2077 whatis_walk_bufctl(uintptr_t baddr
, const umem_bufctl_t
*bcp
, whatis_info_t
*wi
)
2079 mdb_whatis_t
*w
= wi
->wi_w
;
2082 uintptr_t addr
= (uintptr_t)bcp
->bc_addr
;
2083 size_t size
= wi
->wi_cache
->cache_bufsize
;
2085 while (mdb_whatis_match(w
, addr
, size
, &cur
))
2086 whatis_print_umem(wi
, cur
, addr
, baddr
);
2088 return (WHATIS_WALKRET(w
));
2093 whatis_walk_seg(uintptr_t addr
, const vmem_seg_t
*vs
, whatis_info_t
*wi
)
2095 mdb_whatis_t
*w
= wi
->wi_w
;
2097 size_t size
= vs
->vs_end
- vs
->vs_start
;
2100 /* We're not interested in anything but alloc and free segments */
2101 if (vs
->vs_type
!= VMEM_ALLOC
&& vs
->vs_type
!= VMEM_FREE
)
2104 while (mdb_whatis_match(w
, vs
->vs_start
, size
, &cur
)) {
2105 mdb_whatis_report_object(w
, cur
, vs
->vs_start
, "");
2108 * If we're not printing it seperately, provide the vmem_seg
2109 * pointer if it has a stack trace.
2111 if ((mdb_whatis_flags(w
) & WHATIS_QUIET
) &&
2112 ((mdb_whatis_flags(w
) & WHATIS_BUFCTL
) != 0 ||
2113 (vs
->vs_type
== VMEM_ALLOC
&& vs
->vs_depth
!= 0))) {
2114 mdb_printf("vmem_seg %p ", addr
);
2117 mdb_printf("%s from %s vmem arena",
2118 (vs
->vs_type
== VMEM_ALLOC
) ? "allocated" : "freed",
2119 wi
->wi_vmem
->vm_name
);
2121 if (!mdb_whatis_flags(w
) & WHATIS_QUIET
)
2122 whatis_call_printer(vmem_seg
, addr
);
2127 return (WHATIS_WALKRET(w
));
2131 whatis_walk_vmem(uintptr_t addr
, const vmem_t
*vmem
, whatis_info_t
*wi
)
2133 mdb_whatis_t
*w
= wi
->wi_w
;
2134 const char *nm
= vmem
->vm_name
;
2137 if (mdb_whatis_flags(w
) & WHATIS_VERBOSE
)
2138 mdb_printf("Searching vmem arena %s...\n", nm
);
2140 if (mdb_pwalk("vmem_seg",
2141 (mdb_walk_cb_t
)whatis_walk_seg
, wi
, addr
) == -1) {
2142 mdb_warn("can't walk vmem seg for %p", addr
);
2146 return (WHATIS_WALKRET(w
));
2151 whatis_walk_slab(uintptr_t saddr
, const umem_slab_t
*sp
, whatis_info_t
*wi
)
2153 mdb_whatis_t
*w
= wi
->wi_w
;
2155 /* It must overlap with the slab data, or it's not interesting */
2156 if (mdb_whatis_overlaps(w
,
2157 (uintptr_t)sp
->slab_base
, wi
->wi_slab_size
)) {
2158 wi
->wi_slab_found
++;
2165 whatis_walk_cache(uintptr_t addr
, const umem_cache_t
*c
, whatis_info_t
*wi
)
2167 mdb_whatis_t
*w
= wi
->wi_w
;
2168 char *walk
, *freewalk
;
2172 /* Override the '-b' flag as necessary */
2173 if (!(c
->cache_flags
& UMF_HASH
))
2174 do_bufctl
= FALSE
; /* no bufctls to walk */
2175 else if (c
->cache_flags
& UMF_AUDIT
)
2176 do_bufctl
= TRUE
; /* we always want debugging info */
2178 do_bufctl
= ((mdb_whatis_flags(w
) & WHATIS_BUFCTL
) != 0);
2182 freewalk
= "freectl";
2183 func
= (mdb_walk_cb_t
)whatis_walk_bufctl
;
2186 freewalk
= "freemem";
2187 func
= (mdb_walk_cb_t
)whatis_walk_umem
;
2192 if (mdb_whatis_flags(w
) & WHATIS_VERBOSE
)
2193 mdb_printf("Searching %s...\n", c
->cache_name
);
2196 * If more then two buffers live on each slab, figure out if we're
2197 * interested in anything in any slab before doing the more expensive
2198 * umem/freemem (bufctl/freectl) walkers.
2200 wi
->wi_slab_size
= c
->cache_slabsize
- c
->cache_maxcolor
;
2201 if (!(c
->cache_flags
& UMF_HASH
))
2202 wi
->wi_slab_size
-= sizeof (umem_slab_t
);
2204 if ((wi
->wi_slab_size
/ c
->cache_chunksize
) > 2) {
2205 wi
->wi_slab_found
= 0;
2206 if (mdb_pwalk("umem_slab", (mdb_walk_cb_t
)whatis_walk_slab
, wi
,
2208 mdb_warn("can't find umem_slab walker");
2211 if (wi
->wi_slab_found
== 0)
2215 wi
->wi_freemem
= FALSE
;
2216 if (mdb_pwalk(walk
, func
, wi
, addr
) == -1) {
2217 mdb_warn("can't find %s walker", walk
);
2221 if (mdb_whatis_done(w
))
2225 * We have searched for allocated memory; now search for freed memory.
2227 if (mdb_whatis_flags(w
) & WHATIS_VERBOSE
)
2228 mdb_printf("Searching %s for free memory...\n", c
->cache_name
);
2230 wi
->wi_freemem
= TRUE
;
2232 if (mdb_pwalk(freewalk
, func
, wi
, addr
) == -1) {
2233 mdb_warn("can't find %s walker", freewalk
);
2237 return (WHATIS_WALKRET(w
));
2241 whatis_walk_touch(uintptr_t addr
, const umem_cache_t
*c
, whatis_info_t
*wi
)
2243 if (c
->cache_arena
== wi
->wi_msb_arena
||
2244 (c
->cache_cflags
& UMC_NOTOUCH
))
2247 return (whatis_walk_cache(addr
, c
, wi
));
2251 whatis_walk_metadata(uintptr_t addr
, const umem_cache_t
*c
, whatis_info_t
*wi
)
2253 if (c
->cache_arena
!= wi
->wi_msb_arena
)
2256 return (whatis_walk_cache(addr
, c
, wi
));
2260 whatis_walk_notouch(uintptr_t addr
, const umem_cache_t
*c
, whatis_info_t
*wi
)
2262 if (c
->cache_arena
== wi
->wi_msb_arena
||
2263 !(c
->cache_cflags
& UMC_NOTOUCH
))
2266 return (whatis_walk_cache(addr
, c
, wi
));
2271 whatis_run_umem(mdb_whatis_t
*w
, void *ignored
)
2275 bzero(&wi
, sizeof (wi
));
2278 /* umem's metadata is allocated from the umem_internal_arena */
2279 if (umem_readvar(&wi
.wi_msb_arena
, "umem_internal_arena") == -1)
2280 mdb_warn("unable to readvar \"umem_internal_arena\"");
2283 * We process umem caches in the following order:
2285 * non-UMC_NOTOUCH, non-metadata (typically the most interesting)
2286 * metadata (can be huge with UMF_AUDIT)
2287 * UMC_NOTOUCH, non-metadata (see umem_walk_all())
2289 if (mdb_walk("umem_cache", (mdb_walk_cb_t
)whatis_walk_touch
,
2291 mdb_walk("umem_cache", (mdb_walk_cb_t
)whatis_walk_metadata
,
2293 mdb_walk("umem_cache", (mdb_walk_cb_t
)whatis_walk_notouch
,
2295 mdb_warn("couldn't find umem_cache walker");
2303 whatis_run_vmem(mdb_whatis_t
*w
, void *ignored
)
2307 bzero(&wi
, sizeof (wi
));
2310 if (mdb_walk("vmem_postfix",
2311 (mdb_walk_cb_t
)whatis_walk_vmem
, &wi
) == -1) {
2312 mdb_warn("couldn't find vmem_postfix walker");
2322 "umem_cache", "walk list of umem caches", umem_cache_walk_init
,
2323 umem_cache_walk_step
, umem_cache_walk_fini
2326 if (mdb_add_walker(&w
) == -1) {
2327 mdb_warn("failed to add umem_cache walker");
2331 if (umem_update_variables() == -1)
2334 /* install a callback so that our variables are always up-to-date */
2335 (void) mdb_callback_add(MDB_CALLBACK_STCHG
, umem_statechange_cb
, NULL
);
2336 umem_statechange_cb(NULL
);
2339 * Register our ::whatis callbacks.
2341 mdb_whatis_register("umem", whatis_run_umem
, NULL
,
2342 WHATIS_PRIO_ALLOCATOR
, WHATIS_REG_NO_ID
);
2343 mdb_whatis_register("vmem", whatis_run_vmem
, NULL
,
2344 WHATIS_PRIO_ALLOCATOR
, WHATIS_REG_NO_ID
);
2349 typedef struct umem_log_cpu
{
2355 umem_log_walk(uintptr_t addr
, const umem_bufctl_audit_t
*b
, umem_log_cpu_t
*umc
)
2359 for (i
= 0; i
< umem_max_ncpus
; i
++) {
2360 if (addr
>= umc
[i
].umc_low
&& addr
< umc
[i
].umc_high
)
2364 if (i
== umem_max_ncpus
)
2367 mdb_printf("%3d", i
);
2369 mdb_printf(" %0?p %0?p %16llx %0?p\n", addr
, b
->bc_addr
,
2370 b
->bc_timestamp
, b
->bc_thread
);
2377 umem_log(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2379 umem_log_header_t lh
;
2380 umem_cpu_log_header_t clh
;
2381 uintptr_t lhp
, clhp
;
2382 umem_log_cpu_t
*umc
;
2385 if (umem_readvar(&lhp
, "umem_transaction_log") == -1) {
2386 mdb_warn("failed to read 'umem_transaction_log'");
2390 if (lhp
== (uintptr_t)NULL
) {
2391 mdb_warn("no umem transaction log\n");
2395 if (mdb_vread(&lh
, sizeof (umem_log_header_t
), lhp
) == -1) {
2396 mdb_warn("failed to read log header at %p", lhp
);
2400 clhp
= lhp
+ ((uintptr_t)&lh
.lh_cpu
[0] - (uintptr_t)&lh
);
2402 umc
= mdb_zalloc(sizeof (umem_log_cpu_t
) * umem_max_ncpus
,
2405 for (i
= 0; i
< umem_max_ncpus
; i
++) {
2406 if (mdb_vread(&clh
, sizeof (clh
), clhp
) == -1) {
2407 mdb_warn("cannot read cpu %d's log header at %p",
2412 umc
[i
].umc_low
= clh
.clh_chunk
* lh
.lh_chunksize
+
2413 (uintptr_t)lh
.lh_base
;
2414 umc
[i
].umc_high
= (uintptr_t)clh
.clh_current
;
2416 clhp
+= sizeof (umem_cpu_log_header_t
);
2419 if (DCMD_HDRSPEC(flags
)) {
2420 mdb_printf("%3s %-?s %-?s %16s %-?s\n", "CPU", "ADDR",
2421 "BUFADDR", "TIMESTAMP", "THREAD");
2425 * If we have been passed an address, we'll just print out that
2428 if (flags
& DCMD_ADDRSPEC
) {
2429 umem_bufctl_audit_t
*bp
;
2430 UMEM_LOCAL_BUFCTL_AUDIT(&bp
);
2432 if (mdb_vread(bp
, UMEM_BUFCTL_AUDIT_SIZE
, addr
) == -1) {
2433 mdb_warn("failed to read bufctl at %p", addr
);
2437 (void) umem_log_walk(addr
, bp
, umc
);
2442 if (mdb_walk("umem_log", (mdb_walk_cb_t
)umem_log_walk
, umc
) == -1) {
2443 mdb_warn("can't find umem log walker");
2450 typedef struct bufctl_history_cb
{
2453 const mdb_arg_t
*bhc_argv
;
2455 } bufctl_history_cb_t
;
2459 bufctl_history_callback(uintptr_t addr
, const void *ign
, void *arg
)
2461 bufctl_history_cb_t
*bhc
= arg
;
2464 bufctl(addr
, bhc
->bhc_flags
, bhc
->bhc_argc
, bhc
->bhc_argv
);
2466 bhc
->bhc_flags
&= ~DCMD_LOOPFIRST
;
2468 return ((bhc
->bhc_ret
== DCMD_OK
)? WALK_NEXT
: WALK_DONE
);
2475 "Display the contents of umem_bufctl_audit_ts, with optional filtering.\n");
2477 mdb_printf("%<b>OPTIONS%</b>\n");
2480 " -v Display the full content of the bufctl, including its stack trace\n"
2481 " -h retrieve the bufctl's transaction history, if available\n"
2483 " filter out bufctls not involving the buffer at addr\n"
2485 " filter out bufctls without the function/PC in their stack trace\n"
2487 " filter out bufctls timestamped before earliest\n"
2489 " filter out bufctls timestamped after latest\n"
2491 " filter out bufctls not involving thread\n");
2495 bufctl(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2497 uint_t verbose
= FALSE
;
2498 uint_t history
= FALSE
;
2499 uint_t in_history
= FALSE
;
2500 uintptr_t caller
= (uintptr_t)NULL
, thread
= (uintptr_t)NULL
;
2501 uintptr_t laddr
, haddr
, baddr
= (uintptr_t)NULL
;
2502 hrtime_t earliest
= 0, latest
= 0;
2504 char c
[MDB_SYM_NAMLEN
];
2506 umem_bufctl_audit_t
*bcp
;
2507 UMEM_LOCAL_BUFCTL_AUDIT(&bcp
);
2509 if (mdb_getopts(argc
, argv
,
2510 'v', MDB_OPT_SETBITS
, TRUE
, &verbose
,
2511 'h', MDB_OPT_SETBITS
, TRUE
, &history
,
2512 'H', MDB_OPT_SETBITS
, TRUE
, &in_history
, /* internal */
2513 'c', MDB_OPT_UINTPTR
, &caller
,
2514 't', MDB_OPT_UINTPTR
, &thread
,
2515 'e', MDB_OPT_UINT64
, &earliest
,
2516 'l', MDB_OPT_UINT64
, &latest
,
2517 'a', MDB_OPT_UINTPTR
, &baddr
, NULL
) != argc
)
2518 return (DCMD_USAGE
);
2520 if (!(flags
& DCMD_ADDRSPEC
))
2521 return (DCMD_USAGE
);
2523 if (in_history
&& !history
)
2524 return (DCMD_USAGE
);
2526 if (history
&& !in_history
) {
2527 mdb_arg_t
*nargv
= mdb_zalloc(sizeof (*nargv
) * (argc
+ 1),
2529 bufctl_history_cb_t bhc
;
2531 nargv
[0].a_type
= MDB_TYPE_STRING
;
2532 nargv
[0].a_un
.a_str
= "-H"; /* prevent recursion */
2534 for (i
= 0; i
< argc
; i
++)
2535 nargv
[i
+ 1] = argv
[i
];
2538 * When in history mode, we treat each element as if it
2539 * were in a seperate loop, so that the headers group
2540 * bufctls with similar histories.
2542 bhc
.bhc_flags
= flags
| DCMD_LOOP
| DCMD_LOOPFIRST
;
2543 bhc
.bhc_argc
= argc
+ 1;
2544 bhc
.bhc_argv
= nargv
;
2545 bhc
.bhc_ret
= DCMD_OK
;
2547 if (mdb_pwalk("bufctl_history", bufctl_history_callback
, &bhc
,
2549 mdb_warn("unable to walk bufctl_history");
2553 if (bhc
.bhc_ret
== DCMD_OK
&& !(flags
& DCMD_PIPE_OUT
))
2556 return (bhc
.bhc_ret
);
2559 if (DCMD_HDRSPEC(flags
) && !(flags
& DCMD_PIPE_OUT
)) {
2561 mdb_printf("%16s %16s %16s %16s\n"
2562 "%<u>%16s %16s %16s %16s%</u>\n",
2563 "ADDR", "BUFADDR", "TIMESTAMP", "THREAD",
2564 "", "CACHE", "LASTLOG", "CONTENTS");
2566 mdb_printf("%<u>%-?s %-?s %-12s %5s %s%</u>\n",
2567 "ADDR", "BUFADDR", "TIMESTAMP", "THRD", "CALLER");
2571 if (mdb_vread(bcp
, UMEM_BUFCTL_AUDIT_SIZE
, addr
) == -1) {
2572 mdb_warn("couldn't read bufctl at %p", addr
);
2577 * Guard against bogus bc_depth in case the bufctl is corrupt or
2578 * the address does not really refer to a bufctl.
2580 depth
= MIN(bcp
->bc_depth
, umem_stack_depth
);
2582 if (caller
!= (uintptr_t)NULL
) {
2584 haddr
= caller
+ sizeof (caller
);
2586 if (mdb_lookup_by_addr(caller
, MDB_SYM_FUZZY
, c
, sizeof (c
),
2587 &sym
) != -1 && caller
== (uintptr_t)sym
.st_value
) {
2589 * We were provided an exact symbol value; any
2590 * address in the function is valid.
2592 laddr
= (uintptr_t)sym
.st_value
;
2593 haddr
= (uintptr_t)sym
.st_value
+ sym
.st_size
;
2596 for (i
= 0; i
< depth
; i
++)
2597 if (bcp
->bc_stack
[i
] >= laddr
&&
2598 bcp
->bc_stack
[i
] < haddr
)
2605 if (thread
!= (uintptr_t)NULL
&& (uintptr_t)bcp
->bc_thread
!= thread
)
2608 if (earliest
!= 0 && bcp
->bc_timestamp
< earliest
)
2611 if (latest
!= 0 && bcp
->bc_timestamp
> latest
)
2614 if (baddr
!= (uintptr_t)NULL
&& (uintptr_t)bcp
->bc_addr
!= baddr
)
2617 if (flags
& DCMD_PIPE_OUT
) {
2618 mdb_printf("%#r\n", addr
);
2624 "%<b>%16p%</b> %16p %16llx %16d\n"
2625 "%16s %16p %16p %16p\n",
2626 addr
, bcp
->bc_addr
, bcp
->bc_timestamp
, bcp
->bc_thread
,
2627 "", bcp
->bc_cache
, bcp
->bc_lastlog
, bcp
->bc_contents
);
2630 for (i
= 0; i
< depth
; i
++)
2631 mdb_printf("%a\n", bcp
->bc_stack
[i
]);
2635 mdb_printf("%0?p %0?p %12llx %5d", addr
, bcp
->bc_addr
,
2636 bcp
->bc_timestamp
, bcp
->bc_thread
);
2638 for (i
= 0; i
< depth
; i
++) {
2639 if (mdb_lookup_by_addr(bcp
->bc_stack
[i
],
2640 MDB_SYM_FUZZY
, c
, sizeof (c
), &sym
) == -1)
2642 if (is_umem_sym(c
, "umem_"))
2644 mdb_printf(" %a\n", bcp
->bc_stack
[i
]);
2657 bufctl_audit(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2661 if (!(flags
& DCMD_ADDRSPEC
))
2662 return (DCMD_USAGE
);
2665 return (DCMD_USAGE
);
2667 a
.a_type
= MDB_TYPE_STRING
;
2668 a
.a_un
.a_str
= "-v";
2670 return (bufctl(addr
, flags
, 1, &a
));
2673 typedef struct umem_verify
{
2674 uint64_t *umv_buf
; /* buffer to read cache contents into */
2675 size_t umv_size
; /* number of bytes in umv_buf */
2676 int umv_corruption
; /* > 0 if corruption found. */
2677 int umv_besilent
; /* report actual corruption sites */
2678 struct umem_cache umv_cache
; /* the cache we're operating on */
2683 * verify that buf is filled with the pattern pat.
2686 verify_pattern(uint64_t *buf_arg
, size_t size
, uint64_t pat
)
2689 uint64_t *bufend
= (uint64_t *)((char *)buf_arg
+ size
);
2692 for (buf
= buf_arg
; buf
< bufend
; buf
++)
2694 return ((uintptr_t)buf
- (uintptr_t)buf_arg
);
2700 * verify that btp->bt_bxstat == (bcp ^ pat)
2703 verify_buftag(umem_buftag_t
*btp
, uintptr_t pat
)
2705 return (btp
->bt_bxstat
== ((intptr_t)btp
->bt_bufctl
^ pat
) ? 0 : -1);
2710 * verify the integrity of a free block of memory by checking
2711 * that it is filled with 0xdeadbeef and that its buftag is sane.
2715 verify_free(uintptr_t addr
, const void *data
, void *private)
2717 umem_verify_t
*umv
= (umem_verify_t
*)private;
2718 uint64_t *buf
= umv
->umv_buf
; /* buf to validate */
2719 int64_t corrupt
; /* corruption offset */
2720 umem_buftag_t
*buftagp
; /* ptr to buftag */
2721 umem_cache_t
*cp
= &umv
->umv_cache
;
2722 int besilent
= umv
->umv_besilent
;
2725 buftagp
= UMEM_BUFTAG(cp
, buf
);
2728 * Read the buffer to check.
2730 if (mdb_vread(buf
, umv
->umv_size
, addr
) == -1) {
2732 mdb_warn("couldn't read %p", addr
);
2736 if ((corrupt
= verify_pattern(buf
, cp
->cache_verify
,
2737 UMEM_FREE_PATTERN
)) >= 0) {
2739 mdb_printf("buffer %p (free) seems corrupted, at %p\n",
2740 addr
, (uintptr_t)addr
+ corrupt
);
2744 if ((cp
->cache_flags
& UMF_HASH
) &&
2745 buftagp
->bt_redzone
!= UMEM_REDZONE_PATTERN
) {
2747 mdb_printf("buffer %p (free) seems to "
2748 "have a corrupt redzone pattern\n", addr
);
2753 * confirm bufctl pointer integrity.
2755 if (verify_buftag(buftagp
, UMEM_BUFTAG_FREE
) == -1) {
2757 mdb_printf("buffer %p (free) has a corrupt "
2764 umv
->umv_corruption
++;
2770 * Verify that the buftag of an allocated buffer makes sense with respect
2775 verify_alloc(uintptr_t addr
, const void *data
, void *private)
2777 umem_verify_t
*umv
= (umem_verify_t
*)private;
2778 umem_cache_t
*cp
= &umv
->umv_cache
;
2779 uint64_t *buf
= umv
->umv_buf
; /* buf to validate */
2781 umem_buftag_t
*buftagp
= UMEM_BUFTAG(cp
, buf
);
2782 uint32_t *ip
= (uint32_t *)buftagp
;
2783 uint8_t *bp
= (uint8_t *)buf
;
2784 int looks_ok
= 0, size_ok
= 1; /* flags for finding corruption */
2785 int besilent
= umv
->umv_besilent
;
2788 * Read the buffer to check.
2790 if (mdb_vread(buf
, umv
->umv_size
, addr
) == -1) {
2792 mdb_warn("couldn't read %p", addr
);
2797 * There are two cases to handle:
2798 * 1. If the buf was alloc'd using umem_cache_alloc, it will have
2799 * 0xfeedfacefeedface at the end of it
2800 * 2. If the buf was alloc'd using umem_alloc, it will have
2801 * 0xbb just past the end of the region in use. At the buftag,
2802 * it will have 0xfeedface (or, if the whole buffer is in use,
2803 * 0xfeedface & bb000000 or 0xfeedfacf & 000000bb depending on
2804 * endianness), followed by 32 bits containing the offset of the
2805 * 0xbb byte in the buffer.
2807 * Finally, the two 32-bit words that comprise the second half of the
2808 * buftag should xor to UMEM_BUFTAG_ALLOC
2811 if (buftagp
->bt_redzone
== UMEM_REDZONE_PATTERN
)
2813 else if (!UMEM_SIZE_VALID(ip
[1]))
2815 else if (bp
[UMEM_SIZE_DECODE(ip
[1])] == UMEM_REDZONE_BYTE
)
2822 mdb_printf("buffer %p (allocated) has a corrupt "
2823 "redzone size encoding\n", addr
);
2829 mdb_printf("buffer %p (allocated) has a corrupt "
2830 "redzone signature\n", addr
);
2834 if (verify_buftag(buftagp
, UMEM_BUFTAG_ALLOC
) == -1) {
2836 mdb_printf("buffer %p (allocated) has a "
2837 "corrupt buftag\n", addr
);
2843 umv
->umv_corruption
++;
2849 umem_verify(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
2851 if (flags
& DCMD_ADDRSPEC
) {
2852 int check_alloc
= 0, check_free
= 0;
2855 if (mdb_vread(&umv
.umv_cache
, sizeof (umv
.umv_cache
),
2857 mdb_warn("couldn't read umem_cache %p", addr
);
2861 umv
.umv_size
= umv
.umv_cache
.cache_buftag
+
2862 sizeof (umem_buftag_t
);
2863 umv
.umv_buf
= mdb_alloc(umv
.umv_size
, UM_SLEEP
| UM_GC
);
2864 umv
.umv_corruption
= 0;
2866 if ((umv
.umv_cache
.cache_flags
& UMF_REDZONE
)) {
2868 if (umv
.umv_cache
.cache_flags
& UMF_DEADBEEF
)
2871 if (!(flags
& DCMD_LOOP
)) {
2872 mdb_warn("cache %p (%s) does not have "
2873 "redzone checking enabled\n", addr
,
2874 umv
.umv_cache
.cache_name
);
2879 if (flags
& DCMD_LOOP
) {
2881 * table mode, don't print out every corrupt buffer
2883 umv
.umv_besilent
= 1;
2885 mdb_printf("Summary for cache '%s'\n",
2886 umv
.umv_cache
.cache_name
);
2888 umv
.umv_besilent
= 0;
2892 (void) mdb_pwalk("umem", verify_alloc
, &umv
, addr
);
2894 (void) mdb_pwalk("freemem", verify_free
, &umv
, addr
);
2896 if (flags
& DCMD_LOOP
) {
2897 if (umv
.umv_corruption
== 0) {
2898 mdb_printf("%-*s %?p clean\n",
2900 umv
.umv_cache
.cache_name
, addr
);
2902 char *s
= ""; /* optional s in "buffer[s]" */
2903 if (umv
.umv_corruption
> 1)
2906 mdb_printf("%-*s %?p %d corrupt buffer%s\n",
2908 umv
.umv_cache
.cache_name
, addr
,
2909 umv
.umv_corruption
, s
);
2913 * This is the more verbose mode, when the user has
2914 * type addr::umem_verify. If the cache was clean,
2915 * nothing will have yet been printed. So say something.
2917 if (umv
.umv_corruption
== 0)
2918 mdb_printf("clean\n");
2924 * If the user didn't specify a cache to verify, we'll walk all
2925 * umem_cache's, specifying ourself as a callback for each...
2926 * this is the equivalent of '::walk umem_cache .::umem_verify'
2928 mdb_printf("%<u>%-*s %-?s %-20s%</b>\n", UMEM_CACHE_NAMELEN
,
2929 "Cache Name", "Addr", "Cache Integrity");
2930 (void) (mdb_walk_dcmd("umem_cache", "umem_verify", 0, NULL
));
2936 typedef struct vmem_node
{
2937 struct vmem_node
*vn_next
;
2938 struct vmem_node
*vn_parent
;
2939 struct vmem_node
*vn_sibling
;
2940 struct vmem_node
*vn_children
;
2946 typedef struct vmem_walk
{
2947 vmem_node_t
*vw_root
;
2948 vmem_node_t
*vw_current
;
2952 vmem_walk_init(mdb_walk_state_t
*wsp
)
2954 uintptr_t vaddr
, paddr
;
2955 vmem_node_t
*head
= NULL
, *root
= NULL
, *current
= NULL
, *parent
, *vp
;
2958 if (umem_readvar(&vaddr
, "vmem_list") == -1) {
2959 mdb_warn("couldn't read 'vmem_list'");
2963 while (vaddr
!= (uintptr_t)NULL
) {
2964 vp
= mdb_zalloc(sizeof (vmem_node_t
), UM_SLEEP
);
2965 vp
->vn_addr
= vaddr
;
2969 if (vaddr
== wsp
->walk_addr
)
2972 if (mdb_vread(&vp
->vn_vmem
, sizeof (vmem_t
), vaddr
) == -1) {
2973 mdb_warn("couldn't read vmem_t at %p", vaddr
);
2977 vaddr
= (uintptr_t)vp
->vn_vmem
.vm_next
;
2980 for (vp
= head
; vp
!= NULL
; vp
= vp
->vn_next
) {
2982 if ((paddr
= (uintptr_t)vp
->vn_vmem
.vm_source
) ==
2984 vp
->vn_sibling
= root
;
2989 for (parent
= head
; parent
!= NULL
; parent
= parent
->vn_next
) {
2990 if (parent
->vn_addr
!= paddr
)
2992 vp
->vn_sibling
= parent
->vn_children
;
2993 parent
->vn_children
= vp
;
2994 vp
->vn_parent
= parent
;
2998 if (parent
== NULL
) {
2999 mdb_warn("couldn't find %p's parent (%p)\n",
3000 vp
->vn_addr
, paddr
);
3005 vw
= mdb_zalloc(sizeof (vmem_walk_t
), UM_SLEEP
);
3008 if (current
!= NULL
)
3009 vw
->vw_current
= current
;
3011 vw
->vw_current
= root
;
3013 wsp
->walk_data
= vw
;
3016 for (vp
= head
; head
!= NULL
; vp
= head
) {
3018 mdb_free(vp
, sizeof (vmem_node_t
));
3025 vmem_walk_step(mdb_walk_state_t
*wsp
)
3027 vmem_walk_t
*vw
= wsp
->walk_data
;
3031 if ((vp
= vw
->vw_current
) == NULL
)
3034 rval
= wsp
->walk_callback(vp
->vn_addr
, &vp
->vn_vmem
, wsp
->walk_cbdata
);
3036 if (vp
->vn_children
!= NULL
) {
3037 vw
->vw_current
= vp
->vn_children
;
3042 vw
->vw_current
= vp
->vn_sibling
;
3044 } while (vw
->vw_current
== NULL
&& vp
!= NULL
);
3050 * The "vmem_postfix" walk walks the vmem arenas in post-fix order; all
3051 * children are visited before their parent. We perform the postfix walk
3052 * iteratively (rather than recursively) to allow mdb to regain control
3053 * after each callback.
3056 vmem_postfix_walk_step(mdb_walk_state_t
*wsp
)
3058 vmem_walk_t
*vw
= wsp
->walk_data
;
3059 vmem_node_t
*vp
= vw
->vw_current
;
3063 * If this node is marked, then we know that we have already visited
3064 * all of its children. If the node has any siblings, they need to
3065 * be visited next; otherwise, we need to visit the parent. Note
3066 * that vp->vn_marked will only be zero on the first invocation of
3067 * the step function.
3069 if (vp
->vn_marked
) {
3070 if (vp
->vn_sibling
!= NULL
)
3071 vp
= vp
->vn_sibling
;
3072 else if (vp
->vn_parent
!= NULL
)
3076 * We have neither a parent, nor a sibling, and we
3077 * have already been visited; we're done.
3084 * Before we visit this node, visit its children.
3086 while (vp
->vn_children
!= NULL
&& !vp
->vn_children
->vn_marked
)
3087 vp
= vp
->vn_children
;
3090 vw
->vw_current
= vp
;
3091 rval
= wsp
->walk_callback(vp
->vn_addr
, &vp
->vn_vmem
, wsp
->walk_cbdata
);
3097 vmem_walk_fini(mdb_walk_state_t
*wsp
)
3099 vmem_walk_t
*vw
= wsp
->walk_data
;
3100 vmem_node_t
*root
= vw
->vw_root
;
3106 if ((vw
->vw_root
= root
->vn_children
) != NULL
)
3107 vmem_walk_fini(wsp
);
3109 vw
->vw_root
= root
->vn_sibling
;
3110 done
= (root
->vn_sibling
== NULL
&& root
->vn_parent
== NULL
);
3111 mdb_free(root
, sizeof (vmem_node_t
));
3114 mdb_free(vw
, sizeof (vmem_walk_t
));
3116 vmem_walk_fini(wsp
);
3120 typedef struct vmem_seg_walk
{
3122 uintptr_t vsw_start
;
3123 uintptr_t vsw_current
;
3128 vmem_seg_walk_common_init(mdb_walk_state_t
*wsp
, uint8_t type
, char *name
)
3130 vmem_seg_walk_t
*vsw
;
3132 if (wsp
->walk_addr
== (uintptr_t)NULL
) {
3133 mdb_warn("vmem_%s does not support global walks\n", name
);
3137 wsp
->walk_data
= vsw
= mdb_alloc(sizeof (vmem_seg_walk_t
), UM_SLEEP
);
3139 vsw
->vsw_type
= type
;
3140 vsw
->vsw_start
= wsp
->walk_addr
+ OFFSETOF(vmem_t
, vm_seg0
);
3141 vsw
->vsw_current
= vsw
->vsw_start
;
3147 * vmem segments can't have type 0 (this should be added to vmem_impl.h).
3152 vmem_alloc_walk_init(mdb_walk_state_t
*wsp
)
3154 return (vmem_seg_walk_common_init(wsp
, VMEM_ALLOC
, "alloc"));
3158 vmem_free_walk_init(mdb_walk_state_t
*wsp
)
3160 return (vmem_seg_walk_common_init(wsp
, VMEM_FREE
, "free"));
3164 vmem_span_walk_init(mdb_walk_state_t
*wsp
)
3166 return (vmem_seg_walk_common_init(wsp
, VMEM_SPAN
, "span"));
3170 vmem_seg_walk_init(mdb_walk_state_t
*wsp
)
3172 return (vmem_seg_walk_common_init(wsp
, VMEM_NONE
, "seg"));
3176 vmem_seg_walk_step(mdb_walk_state_t
*wsp
)
3179 vmem_seg_walk_t
*vsw
= wsp
->walk_data
;
3180 uintptr_t addr
= vsw
->vsw_current
;
3181 static size_t seg_size
= 0;
3185 if (umem_readvar(&seg_size
, "vmem_seg_size") == -1) {
3186 mdb_warn("failed to read 'vmem_seg_size'");
3187 seg_size
= sizeof (vmem_seg_t
);
3191 if (seg_size
< sizeof (seg
))
3192 bzero((caddr_t
)&seg
+ seg_size
, sizeof (seg
) - seg_size
);
3194 if (mdb_vread(&seg
, seg_size
, addr
) == -1) {
3195 mdb_warn("couldn't read vmem_seg at %p", addr
);
3199 vsw
->vsw_current
= (uintptr_t)seg
.vs_anext
;
3200 if (vsw
->vsw_type
!= VMEM_NONE
&& seg
.vs_type
!= vsw
->vsw_type
) {
3203 rval
= wsp
->walk_callback(addr
, &seg
, wsp
->walk_cbdata
);
3206 if (vsw
->vsw_current
== vsw
->vsw_start
)
3213 vmem_seg_walk_fini(mdb_walk_state_t
*wsp
)
3215 vmem_seg_walk_t
*vsw
= wsp
->walk_data
;
3217 mdb_free(vsw
, sizeof (vmem_seg_walk_t
));
3220 #define VMEM_NAMEWIDTH 22
3223 vmem(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
3228 char c
[VMEM_NAMEWIDTH
];
3230 if (!(flags
& DCMD_ADDRSPEC
)) {
3231 if (mdb_walk_dcmd("vmem", "vmem", argc
, argv
) == -1) {
3232 mdb_warn("can't walk vmem");
3238 if (DCMD_HDRSPEC(flags
))
3239 mdb_printf("%-?s %-*s %10s %12s %9s %5s\n",
3240 "ADDR", VMEM_NAMEWIDTH
, "NAME", "INUSE",
3241 "TOTAL", "SUCCEED", "FAIL");
3243 if (mdb_vread(&v
, sizeof (v
), addr
) == -1) {
3244 mdb_warn("couldn't read vmem at %p", addr
);
3248 for (paddr
= (uintptr_t)v
.vm_source
;
3249 paddr
!= (uintptr_t)NULL
; ident
+= 2) {
3250 if (mdb_vread(&parent
, sizeof (parent
), paddr
) == -1) {
3251 mdb_warn("couldn't trace %p's ancestry", addr
);
3255 paddr
= (uintptr_t)parent
.vm_source
;
3258 (void) mdb_snprintf(c
, VMEM_NAMEWIDTH
, "%*s%s", ident
, "", v
.vm_name
);
3260 mdb_printf("%0?p %-*s %10llu %12llu %9llu %5llu\n",
3261 addr
, VMEM_NAMEWIDTH
, c
,
3262 v
.vm_kstat
.vk_mem_inuse
, v
.vm_kstat
.vk_mem_total
,
3263 v
.vm_kstat
.vk_alloc
, v
.vm_kstat
.vk_fail
);
3272 "Display the contents of vmem_seg_ts, with optional filtering.\n"
3274 "A vmem_seg_t represents a range of addresses (or arbitrary numbers),\n"
3275 "representing a single chunk of data. Only ALLOC segments have debugging\n"
3278 mdb_printf("%<b>OPTIONS%</b>\n");
3281 " -v Display the full content of the vmem_seg, including its stack trace\n"
3282 " -s report the size of the segment, instead of the end address\n"
3284 " filter out segments without the function/PC in their stack trace\n"
3286 " filter out segments timestamped before earliest\n"
3288 " filter out segments timestamped after latest\n"
3290 " filer out segments smaller than minsize\n"
3292 " filer out segments larger than maxsize\n"
3294 " filter out segments not involving thread\n"
3296 " filter out segments not of type 'type'\n"
3297 " type is one of: ALLOC/FREE/SPAN/ROTOR/WALKER\n");
3303 vmem_seg(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
3306 uintptr_t *stk
= vs
.vs_stack
;
3309 const char *type
= NULL
;
3311 char c
[MDB_SYM_NAMLEN
];
3315 uintptr_t laddr
, haddr
;
3317 uintptr_t caller
= (uintptr_t)NULL
, thread
= (uintptr_t)NULL
;
3318 uintptr_t minsize
= 0, maxsize
= 0;
3320 hrtime_t earliest
= 0, latest
= 0;
3325 if (!(flags
& DCMD_ADDRSPEC
))
3326 return (DCMD_USAGE
);
3328 if (mdb_getopts(argc
, argv
,
3329 'c', MDB_OPT_UINTPTR
, &caller
,
3330 'e', MDB_OPT_UINT64
, &earliest
,
3331 'l', MDB_OPT_UINT64
, &latest
,
3332 's', MDB_OPT_SETBITS
, TRUE
, &size
,
3333 'm', MDB_OPT_UINTPTR
, &minsize
,
3334 'M', MDB_OPT_UINTPTR
, &maxsize
,
3335 't', MDB_OPT_UINTPTR
, &thread
,
3336 'T', MDB_OPT_STR
, &type
,
3337 'v', MDB_OPT_SETBITS
, TRUE
, &verbose
,
3339 return (DCMD_USAGE
);
3341 if (DCMD_HDRSPEC(flags
) && !(flags
& DCMD_PIPE_OUT
)) {
3343 mdb_printf("%16s %4s %16s %16s %16s\n"
3344 "%<u>%16s %4s %16s %16s %16s%</u>\n",
3345 "ADDR", "TYPE", "START", "END", "SIZE",
3346 "", "", "THREAD", "TIMESTAMP", "");
3348 mdb_printf("%?s %4s %?s %?s %s\n", "ADDR", "TYPE",
3349 "START", size
? "SIZE" : "END", "WHO");
3353 if (mdb_vread(&vs
, sizeof (vs
), addr
) == -1) {
3354 mdb_warn("couldn't read vmem_seg at %p", addr
);
3359 if (strcmp(type
, "ALLC") == 0 || strcmp(type
, "ALLOC") == 0)
3361 else if (strcmp(type
, "FREE") == 0)
3363 else if (strcmp(type
, "SPAN") == 0)
3365 else if (strcmp(type
, "ROTR") == 0 ||
3366 strcmp(type
, "ROTOR") == 0)
3368 else if (strcmp(type
, "WLKR") == 0 ||
3369 strcmp(type
, "WALKER") == 0)
3372 mdb_warn("\"%s\" is not a recognized vmem_seg type\n",
3377 if (vs
.vs_type
!= t
)
3381 sz
= vs
.vs_end
- vs
.vs_start
;
3383 if (minsize
!= 0 && sz
< minsize
)
3386 if (maxsize
!= 0 && sz
> maxsize
)
3390 depth
= vs
.vs_depth
;
3393 * debug info, when present, is only accurate for VMEM_ALLOC segments
3395 no_debug
= (t
!= VMEM_ALLOC
) ||
3396 (depth
== 0 || depth
> VMEM_STACK_DEPTH
);
3399 if (caller
!= (uintptr_t)NULL
|| thread
!= (uintptr_t)NULL
||
3400 earliest
!= 0 || latest
!= 0)
3401 return (DCMD_OK
); /* not enough info */
3403 if (caller
!= (uintptr_t)NULL
) {
3405 haddr
= caller
+ sizeof (caller
);
3407 if (mdb_lookup_by_addr(caller
, MDB_SYM_FUZZY
, c
,
3408 sizeof (c
), &sym
) != -1 &&
3409 caller
== (uintptr_t)sym
.st_value
) {
3411 * We were provided an exact symbol value; any
3412 * address in the function is valid.
3414 laddr
= (uintptr_t)sym
.st_value
;
3415 haddr
= (uintptr_t)sym
.st_value
+ sym
.st_size
;
3418 for (i
= 0; i
< depth
; i
++)
3419 if (vs
.vs_stack
[i
] >= laddr
&&
3420 vs
.vs_stack
[i
] < haddr
)
3427 if (thread
!= (uintptr_t)NULL
&&
3428 (uintptr_t)vs
.vs_thread
!= thread
)
3431 if (earliest
!= 0 && vs
.vs_timestamp
< earliest
)
3434 if (latest
!= 0 && vs
.vs_timestamp
> latest
)
3438 type
= (t
== VMEM_ALLOC
? "ALLC" :
3439 t
== VMEM_FREE
? "FREE" :
3440 t
== VMEM_SPAN
? "SPAN" :
3441 t
== VMEM_ROTOR
? "ROTR" :
3442 t
== VMEM_WALKER
? "WLKR" :
3445 if (flags
& DCMD_PIPE_OUT
) {
3446 mdb_printf("%#r\n", addr
);
3451 mdb_printf("%<b>%16p%</b> %4s %16p %16p %16d\n",
3452 addr
, type
, vs
.vs_start
, vs
.vs_end
, sz
);
3457 mdb_printf("%16s %4s %16d %16llx\n",
3458 "", "", vs
.vs_thread
, vs
.vs_timestamp
);
3461 for (i
= 0; i
< depth
; i
++) {
3462 mdb_printf("%a\n", stk
[i
]);
3467 mdb_printf("%0?p %4s %0?p %0?p", addr
, type
,
3468 vs
.vs_start
, size
? sz
: vs
.vs_end
);
3475 for (i
= 0; i
< depth
; i
++) {
3476 if (mdb_lookup_by_addr(stk
[i
], MDB_SYM_FUZZY
,
3477 c
, sizeof (c
), &sym
) == -1)
3479 if (is_umem_sym(c
, "vmem_"))
3483 mdb_printf(" %a\n", stk
[i
]);
3490 showbc(uintptr_t addr
, const umem_bufctl_audit_t
*bcp
, hrtime_t
*newest
)
3492 char name
[UMEM_CACHE_NAMELEN
+ 1];
3496 if (bcp
->bc_timestamp
== 0)
3500 *newest
= bcp
->bc_timestamp
;
3502 delta
= *newest
- bcp
->bc_timestamp
;
3503 depth
= MIN(bcp
->bc_depth
, umem_stack_depth
);
3505 if (mdb_readstr(name
, sizeof (name
), (uintptr_t)
3506 &bcp
->bc_cache
->cache_name
) <= 0)
3507 (void) mdb_snprintf(name
, sizeof (name
), "%a", bcp
->bc_cache
);
3509 mdb_printf("\nT-%lld.%09lld addr=%p %s\n",
3510 delta
/ NANOSEC
, delta
% NANOSEC
, bcp
->bc_addr
, name
);
3512 for (i
= 0; i
< depth
; i
++)
3513 mdb_printf("\t %a\n", bcp
->bc_stack
[i
]);
3519 umalog(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
3521 const char *logname
= "umem_transaction_log";
3522 hrtime_t newest
= 0;
3524 if ((flags
& DCMD_ADDRSPEC
) || argc
> 1)
3525 return (DCMD_USAGE
);
3528 if (argv
->a_type
!= MDB_TYPE_STRING
)
3529 return (DCMD_USAGE
);
3530 if (strcmp(argv
->a_un
.a_str
, "fail") == 0)
3531 logname
= "umem_failure_log";
3532 else if (strcmp(argv
->a_un
.a_str
, "slab") == 0)
3533 logname
= "umem_slab_log";
3535 return (DCMD_USAGE
);
3538 if (umem_readvar(&addr
, logname
) == -1) {
3539 mdb_warn("failed to read %s log header pointer");
3543 if (mdb_pwalk("umem_log", (mdb_walk_cb_t
)showbc
, &newest
, addr
) == -1) {
3544 mdb_warn("failed to walk umem log");
3552 * As the final lure for die-hard crash(1M) users, we provide ::umausers here.
3553 * The first piece is a structure which we use to accumulate umem_cache_t
3554 * addresses of interest. The umc_add is used as a callback for the umem_cache
3555 * walker; we either add all caches, or ones named explicitly as arguments.
3558 typedef struct umclist
{
3559 const char *umc_name
; /* Name to match (or NULL) */
3560 uintptr_t *umc_caches
; /* List of umem_cache_t addrs */
3561 int umc_nelems
; /* Num entries in umc_caches */
3562 int umc_size
; /* Size of umc_caches array */
3566 umc_add(uintptr_t addr
, const umem_cache_t
*cp
, umclist_t
*umc
)
3571 if (umc
->umc_name
== NULL
||
3572 strcmp(cp
->cache_name
, umc
->umc_name
) == 0) {
3574 * If we have a match, grow our array (if necessary), and then
3575 * add the virtual address of the matching cache to our list.
3577 if (umc
->umc_nelems
>= umc
->umc_size
) {
3578 s
= umc
->umc_size
? umc
->umc_size
* 2 : 256;
3579 p
= mdb_alloc(sizeof (uintptr_t) * s
, UM_SLEEP
| UM_GC
);
3581 bcopy(umc
->umc_caches
, p
,
3582 sizeof (uintptr_t) * umc
->umc_size
);
3584 umc
->umc_caches
= p
;
3588 umc
->umc_caches
[umc
->umc_nelems
++] = addr
;
3589 return (umc
->umc_name
? WALK_DONE
: WALK_NEXT
);
3596 * The second piece of ::umausers is a hash table of allocations. Each
3597 * allocation owner is identified by its stack trace and data_size. We then
3598 * track the total bytes of all such allocations, and the number of allocations
3599 * to report at the end. Once we have a list of caches, we walk through the
3600 * allocated bufctls of each, and update our hash table accordingly.
3603 typedef struct umowner
{
3604 struct umowner
*umo_head
; /* First hash elt in bucket */
3605 struct umowner
*umo_next
; /* Next hash elt in chain */
3606 size_t umo_signature
; /* Hash table signature */
3607 uint_t umo_num
; /* Number of allocations */
3608 size_t umo_data_size
; /* Size of each allocation */
3609 size_t umo_total_size
; /* Total bytes of allocation */
3610 int umo_depth
; /* Depth of stack trace */
3611 uintptr_t *umo_stack
; /* Stack trace */
3614 typedef struct umusers
{
3615 const umem_cache_t
*umu_cache
; /* Current umem cache */
3616 umowner_t
*umu_hash
; /* Hash table of owners */
3617 uintptr_t *umu_stacks
; /* stacks for owners */
3618 int umu_nelems
; /* Number of entries in use */
3619 int umu_size
; /* Total number of entries */
3623 umu_add(umusers_t
*umu
, const umem_bufctl_audit_t
*bcp
,
3624 size_t size
, size_t data_size
)
3626 int i
, depth
= MIN(bcp
->bc_depth
, umem_stack_depth
);
3627 size_t bucket
, signature
= data_size
;
3628 umowner_t
*umo
, *umoend
;
3631 * If the hash table is full, double its size and rehash everything.
3633 if (umu
->umu_nelems
>= umu
->umu_size
) {
3634 int s
= umu
->umu_size
? umu
->umu_size
* 2 : 1024;
3635 size_t umowner_size
= sizeof (umowner_t
);
3636 size_t trace_size
= umem_stack_depth
* sizeof (uintptr_t);
3637 uintptr_t *new_stacks
;
3639 umo
= mdb_alloc(umowner_size
* s
, UM_SLEEP
| UM_GC
);
3640 new_stacks
= mdb_alloc(trace_size
* s
, UM_SLEEP
| UM_GC
);
3642 bcopy(umu
->umu_hash
, umo
, umowner_size
* umu
->umu_size
);
3643 bcopy(umu
->umu_stacks
, new_stacks
, trace_size
* umu
->umu_size
);
3644 umu
->umu_hash
= umo
;
3645 umu
->umu_stacks
= new_stacks
;
3648 umoend
= umu
->umu_hash
+ umu
->umu_size
;
3649 for (umo
= umu
->umu_hash
; umo
< umoend
; umo
++) {
3650 umo
->umo_head
= NULL
;
3651 umo
->umo_stack
= &umu
->umu_stacks
[
3652 umem_stack_depth
* (umo
- umu
->umu_hash
)];
3655 umoend
= umu
->umu_hash
+ umu
->umu_nelems
;
3656 for (umo
= umu
->umu_hash
; umo
< umoend
; umo
++) {
3657 bucket
= umo
->umo_signature
& (umu
->umu_size
- 1);
3658 umo
->umo_next
= umu
->umu_hash
[bucket
].umo_head
;
3659 umu
->umu_hash
[bucket
].umo_head
= umo
;
3664 * Finish computing the hash signature from the stack trace, and then
3665 * see if the owner is in the hash table. If so, update our stats.
3667 for (i
= 0; i
< depth
; i
++)
3668 signature
+= bcp
->bc_stack
[i
];
3670 bucket
= signature
& (umu
->umu_size
- 1);
3672 for (umo
= umu
->umu_hash
[bucket
].umo_head
; umo
; umo
= umo
->umo_next
) {
3673 if (umo
->umo_signature
== signature
) {
3674 size_t difference
= 0;
3676 difference
|= umo
->umo_data_size
- data_size
;
3677 difference
|= umo
->umo_depth
- depth
;
3679 for (i
= 0; i
< depth
; i
++) {
3680 difference
|= umo
->umo_stack
[i
] -
3684 if (difference
== 0) {
3685 umo
->umo_total_size
+= size
;
3693 * If the owner is not yet hashed, grab the next element and fill it
3694 * in based on the allocation information.
3696 umo
= &umu
->umu_hash
[umu
->umu_nelems
++];
3697 umo
->umo_next
= umu
->umu_hash
[bucket
].umo_head
;
3698 umu
->umu_hash
[bucket
].umo_head
= umo
;
3700 umo
->umo_signature
= signature
;
3702 umo
->umo_data_size
= data_size
;
3703 umo
->umo_total_size
= size
;
3704 umo
->umo_depth
= depth
;
3706 for (i
= 0; i
< depth
; i
++)
3707 umo
->umo_stack
[i
] = bcp
->bc_stack
[i
];
3711 * When ::umausers is invoked without the -f flag, we simply update our hash
3712 * table with the information from each allocated bufctl.
3716 umause1(uintptr_t addr
, const umem_bufctl_audit_t
*bcp
, umusers_t
*umu
)
3718 const umem_cache_t
*cp
= umu
->umu_cache
;
3720 umu_add(umu
, bcp
, cp
->cache_bufsize
, cp
->cache_bufsize
);
3725 * When ::umausers is invoked with the -f flag, we print out the information
3726 * for each bufctl as well as updating the hash table.
3729 umause2(uintptr_t addr
, const umem_bufctl_audit_t
*bcp
, umusers_t
*umu
)
3731 int i
, depth
= MIN(bcp
->bc_depth
, umem_stack_depth
);
3732 const umem_cache_t
*cp
= umu
->umu_cache
;
3734 mdb_printf("size %d, addr %p, thread %p, cache %s\n",
3735 cp
->cache_bufsize
, addr
, bcp
->bc_thread
, cp
->cache_name
);
3737 for (i
= 0; i
< depth
; i
++)
3738 mdb_printf("\t %a\n", bcp
->bc_stack
[i
]);
3740 umu_add(umu
, bcp
, cp
->cache_bufsize
, cp
->cache_bufsize
);
3745 * We sort our results by allocation size before printing them.
3748 umownercmp(const void *lp
, const void *rp
)
3750 const umowner_t
*lhs
= lp
;
3751 const umowner_t
*rhs
= rp
;
3753 return (rhs
->umo_total_size
- lhs
->umo_total_size
);
3757 * The main engine of ::umausers is relatively straightforward: First we
3758 * accumulate our list of umem_cache_t addresses into the umclist_t. Next we
3759 * iterate over the allocated bufctls of each cache in the list. Finally,
3760 * we sort and print our results.
3764 umausers(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
3766 int mem_threshold
= 8192; /* Minimum # bytes for printing */
3767 int cnt_threshold
= 100; /* Minimum # blocks for printing */
3768 int audited_caches
= 0; /* Number of UMF_AUDIT caches found */
3769 int do_all_caches
= 1; /* Do all caches (no arguments) */
3770 int opt_e
= FALSE
; /* Include "small" users */
3771 int opt_f
= FALSE
; /* Print stack traces */
3773 mdb_walk_cb_t callback
= (mdb_walk_cb_t
)umause1
;
3774 umowner_t
*umo
, *umoend
;
3780 if (flags
& DCMD_ADDRSPEC
)
3781 return (DCMD_USAGE
);
3783 bzero(&umc
, sizeof (umc
));
3784 bzero(&umu
, sizeof (umu
));
3786 while ((i
= mdb_getopts(argc
, argv
,
3787 'e', MDB_OPT_SETBITS
, TRUE
, &opt_e
,
3788 'f', MDB_OPT_SETBITS
, TRUE
, &opt_f
, NULL
)) != argc
) {
3790 argv
+= i
; /* skip past options we just processed */
3791 argc
-= i
; /* adjust argc */
3793 if (argv
->a_type
!= MDB_TYPE_STRING
|| *argv
->a_un
.a_str
== '-')
3794 return (DCMD_USAGE
);
3796 oelems
= umc
.umc_nelems
;
3797 umc
.umc_name
= argv
->a_un
.a_str
;
3798 (void) mdb_walk("umem_cache", (mdb_walk_cb_t
)umc_add
, &umc
);
3800 if (umc
.umc_nelems
== oelems
) {
3801 mdb_warn("unknown umem cache: %s\n", umc
.umc_name
);
3811 mem_threshold
= cnt_threshold
= 0;
3814 callback
= (mdb_walk_cb_t
)umause2
;
3816 if (do_all_caches
) {
3817 umc
.umc_name
= NULL
; /* match all cache names */
3818 (void) mdb_walk("umem_cache", (mdb_walk_cb_t
)umc_add
, &umc
);
3821 for (i
= 0; i
< umc
.umc_nelems
; i
++) {
3822 uintptr_t cp
= umc
.umc_caches
[i
];
3825 if (mdb_vread(&c
, sizeof (c
), cp
) == -1) {
3826 mdb_warn("failed to read cache at %p", cp
);
3830 if (!(c
.cache_flags
& UMF_AUDIT
)) {
3831 if (!do_all_caches
) {
3832 mdb_warn("UMF_AUDIT is not enabled for %s\n",
3839 (void) mdb_pwalk("bufctl", callback
, &umu
, cp
);
3843 if (audited_caches
== 0 && do_all_caches
) {
3844 mdb_warn("UMF_AUDIT is not enabled for any caches\n");
3848 qsort(umu
.umu_hash
, umu
.umu_nelems
, sizeof (umowner_t
), umownercmp
);
3849 umoend
= umu
.umu_hash
+ umu
.umu_nelems
;
3851 for (umo
= umu
.umu_hash
; umo
< umoend
; umo
++) {
3852 if (umo
->umo_total_size
< mem_threshold
&&
3853 umo
->umo_num
< cnt_threshold
)
3855 mdb_printf("%lu bytes for %u allocations with data size %lu:\n",
3856 umo
->umo_total_size
, umo
->umo_num
, umo
->umo_data_size
);
3857 for (i
= 0; i
< umo
->umo_depth
; i
++)
3858 mdb_printf("\t %a\n", umo
->umo_stack
[i
]);
3864 struct malloc_data
{
3865 uint32_t malloc_size
;
3866 uint32_t malloc_stat
; /* == UMEM_MALLOC_ENCODE(state, malloc_size) */
3870 #define UMI_MAX_BUCKET (UMEM_MAXBUF - 2*sizeof (struct malloc_data))
3872 #define UMI_MAX_BUCKET (UMEM_MAXBUF - sizeof (struct malloc_data))
3875 typedef struct umem_malloc_info
{
3876 size_t um_total
; /* total allocated buffers */
3877 size_t um_malloc
; /* malloc buffers */
3878 size_t um_malloc_size
; /* sum of malloc buffer sizes */
3879 size_t um_malloc_overhead
; /* sum of in-chunk overheads */
3881 umem_cache_t
*um_cp
;
3884 } umem_malloc_info_t
;
3887 umem_malloc_print_dist(uint_t
*um_bucket
, size_t minmalloc
, size_t maxmalloc
,
3888 size_t maxbuckets
, size_t minbucketsize
, int geometric
)
3897 const int *distarray
;
3899 minb
= (int)minmalloc
;
3900 maxb
= (int)maxmalloc
;
3902 nbucks
= buckets
= maxb
- minb
+ 1;
3905 for (b
= minb
; b
<= maxb
; b
++)
3906 um_malloc
+= um_bucket
[b
];
3908 if (maxbuckets
!= 0)
3909 buckets
= MIN(buckets
, maxbuckets
);
3911 if (minbucketsize
> 1) {
3912 buckets
= MIN(buckets
, nbucks
/minbucketsize
);
3915 minbucketsize
= nbucks
;
3920 distarray
= dist_geometric(buckets
, minb
, maxb
, minbucketsize
);
3922 distarray
= dist_linear(buckets
, minb
, maxb
);
3924 dist_print_header("malloc size", 11, "count");
3925 for (i
= 0; i
< buckets
; i
++) {
3926 dist_print_bucket(distarray
, i
, um_bucket
, um_malloc
, 11);
3932 * A malloc()ed buffer looks like:
3934 * <----------- mi.malloc_size --->
3935 * <----------- cp.cache_bufsize ------------------>
3936 * <----------- cp.cache_chunksize -------------------------------->
3937 * +-------+-----------------------+---------------+---------------+
3938 * |/tag///| mallocsz |/round-off/////|/debug info////|
3939 * +-------+---------------------------------------+---------------+
3940 * <-- usable space ------>
3942 * mallocsz is the argument to malloc(3C).
3943 * mi.malloc_size is the actual size passed to umem_alloc(), which
3944 * is rounded up to the smallest available cache size, which is
3945 * cache_bufsize. If there is debugging or alignment overhead in
3946 * the cache, that is reflected in a larger cache_chunksize.
3948 * The tag at the beginning of the buffer is either 8-bytes or 16-bytes,
3949 * depending upon the ISA's alignment requirements. For 32-bit allocations,
3950 * it is always a 8-byte tag. For 64-bit allocations larger than 8 bytes,
3951 * the tag has 8 bytes of padding before it.
3953 * 32-byte, 64-byte buffers <= 8 bytes:
3954 * +-------+-------+--------- ...
3955 * |/size//|/stat//| mallocsz ...
3956 * +-------+-------+--------- ...
3958 * pointer returned from malloc(3C)
3960 * 64-byte buffers > 8 bytes:
3961 * +---------------+-------+-------+--------- ...
3962 * |/padding///////|/size//|/stat//| mallocsz ...
3963 * +---------------+-------+-------+--------- ...
3965 * pointer returned from malloc(3C)
3967 * The "size" field is "malloc_size", which is mallocsz + the padding.
3968 * The "stat" field is derived from malloc_size, and functions as a
3969 * validation that this buffer is actually from malloc(3C).
3973 um_umem_buffer_cb(uintptr_t addr
, void *buf
, umem_malloc_info_t
*ump
)
3975 struct malloc_data md
;
3976 size_t m_addr
= addr
;
3977 size_t overhead
= sizeof (md
);
3983 if (ump
->um_cp
->cache_bufsize
> UMEM_SECOND_ALIGN
) {
3985 overhead
+= sizeof (md
);
3989 if (mdb_vread(&md
, sizeof (md
), m_addr
) == -1) {
3990 mdb_warn("unable to read malloc header at %p", m_addr
);
3994 switch (UMEM_MALLOC_DECODE(md
.malloc_stat
, md
.malloc_size
)) {
3997 case MALLOC_SECOND_MAGIC
:
3999 mallocsz
= md
.malloc_size
- overhead
;
4002 ump
->um_malloc_size
+= mallocsz
;
4003 ump
->um_malloc_overhead
+= overhead
;
4005 /* include round-off and debug overhead */
4006 ump
->um_malloc_overhead
+=
4007 ump
->um_cp
->cache_chunksize
- md
.malloc_size
;
4009 if (ump
->um_bucket
!= NULL
&& mallocsz
<= UMI_MAX_BUCKET
)
4010 ump
->um_bucket
[mallocsz
]++;
4021 get_umem_alloc_sizes(int **out
, size_t *out_num
)
4025 if (umem_lookup_by_name("umem_alloc_sizes", &sym
) == -1) {
4026 mdb_warn("unable to look up umem_alloc_sizes");
4030 *out
= mdb_alloc(sym
.st_size
, UM_SLEEP
| UM_GC
);
4031 *out_num
= sym
.st_size
/ sizeof (int);
4033 if (mdb_vread(*out
, sym
.st_size
, sym
.st_value
) == -1) {
4034 mdb_warn("unable to read umem_alloc_sizes (%p)", sym
.st_value
);
4044 um_umem_cache_cb(uintptr_t addr
, umem_cache_t
*cp
, umem_malloc_info_t
*ump
)
4046 if (strncmp(cp
->cache_name
, "umem_alloc_", strlen("umem_alloc_")) != 0)
4051 if (mdb_pwalk("umem", (mdb_walk_cb_t
)um_umem_buffer_cb
, ump
, addr
) ==
4053 mdb_warn("can't walk 'umem' for cache %p", addr
);
4061 umem_malloc_dist_help(void)
4064 "report distribution of outstanding malloc()s");
4066 mdb_printf("%<b>OPTIONS%</b>\n");
4070 " Use at most maxbins bins for the data\n"
4072 " Make the bins at least minbinsize bytes apart\n"
4073 " -d dump the raw data out, without binning\n"
4074 " -g use geometric binning instead of linear binning\n");
4079 umem_malloc_dist(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
4081 umem_malloc_info_t mi
;
4082 uint_t geometric
= 0;
4084 size_t maxbuckets
= 0;
4085 size_t minbucketsize
= 0;
4087 size_t minalloc
= 0;
4088 size_t maxalloc
= UMI_MAX_BUCKET
;
4090 if (flags
& DCMD_ADDRSPEC
)
4091 return (DCMD_USAGE
);
4093 if (mdb_getopts(argc
, argv
,
4094 'd', MDB_OPT_SETBITS
, TRUE
, &dump
,
4095 'g', MDB_OPT_SETBITS
, TRUE
, &geometric
,
4096 'b', MDB_OPT_UINTPTR
, &maxbuckets
,
4097 'B', MDB_OPT_UINTPTR
, &minbucketsize
,
4099 return (DCMD_USAGE
);
4101 bzero(&mi
, sizeof (mi
));
4102 mi
.um_bucket
= mdb_zalloc((UMI_MAX_BUCKET
+ 1) * sizeof (*mi
.um_bucket
),
4105 if (mdb_walk("umem_cache", (mdb_walk_cb_t
)um_umem_cache_cb
,
4107 mdb_warn("unable to walk 'umem_cache'");
4113 for (i
= minalloc
; i
<= maxalloc
; i
++)
4114 mdb_printf("%d\t%d\n", i
, mi
.um_bucket
[i
]);
4119 umem_malloc_print_dist(mi
.um_bucket
, minalloc
, maxalloc
,
4120 maxbuckets
, minbucketsize
, geometric
);
4126 umem_malloc_info_help(void)
4129 "report information about malloc()s by cache. ");
4131 mdb_printf("%<b>OPTIONS%</b>\n");
4135 " Use at most maxbins bins for the data\n"
4137 " Make the bins at least minbinsize bytes apart\n"
4138 " -d dump the raw distribution data without binning\n"
4140 " -g use geometric binning instead of linear binning\n"
4145 umem_malloc_info(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
4148 umem_malloc_info_t mi
;
4156 size_t overhead_pct
; /* 1000 * overhead_percent */
4160 uint_t geometric
= 0;
4161 size_t maxbuckets
= 0;
4162 size_t minbucketsize
= 0;
4169 if (mdb_getopts(argc
, argv
,
4170 'd', MDB_OPT_SETBITS
, TRUE
, &dump
,
4171 'g', MDB_OPT_SETBITS
, TRUE
, &geometric
,
4172 'b', MDB_OPT_UINTPTR
, &maxbuckets
,
4173 'B', MDB_OPT_UINTPTR
, &minbucketsize
,
4175 return (DCMD_USAGE
);
4177 if (dump
|| geometric
|| (maxbuckets
!= 0) || (minbucketsize
!= 0))
4180 if (!(flags
& DCMD_ADDRSPEC
)) {
4181 if (mdb_walk_dcmd("umem_cache", "umem_malloc_info",
4182 argc
, argv
) == -1) {
4183 mdb_warn("can't walk umem_cache");
4189 if (!mdb_vread(&c
, sizeof (c
), addr
)) {
4190 mdb_warn("unable to read cache at %p", addr
);
4194 if (strncmp(c
.cache_name
, "umem_alloc_", strlen("umem_alloc_")) != 0) {
4195 if (!(flags
& DCMD_LOOP
))
4196 mdb_warn("umem_malloc_info: cache \"%s\" is not used "
4197 "by malloc()\n", c
.cache_name
);
4202 * normally, print the header only the first time. In verbose mode,
4203 * print the header on every non-skipped buffer
4205 if ((!verbose
&& DCMD_HDRSPEC(flags
)) || (verbose
&& !skip
))
4206 mdb_printf("%<ul>%-?s %6s %6s %8s %8s %10s %10s %6s%</ul>\n",
4207 "CACHE", "BUFSZ", "MAXMAL",
4208 "BUFMALLC", "AVG_MAL", "MALLOCED", "OVERHEAD", "%OVER");
4213 maxmalloc
= c
.cache_bufsize
- sizeof (struct malloc_data
);
4215 if (c
.cache_bufsize
> UMEM_SECOND_ALIGN
)
4216 maxmalloc
-= sizeof (struct malloc_data
);
4219 bzero(&mi
, sizeof (mi
));
4223 mdb_zalloc((UMI_MAX_BUCKET
+ 1) * sizeof (*mi
.um_bucket
),
4226 if (mdb_pwalk("umem", (mdb_walk_cb_t
)um_umem_buffer_cb
, &mi
, addr
) ==
4228 mdb_warn("can't walk 'umem'");
4232 overhead
= mi
.um_malloc_overhead
;
4233 allocated
= mi
.um_malloc_size
;
4235 /* do integer round off for the average */
4236 if (mi
.um_malloc
!= 0)
4237 avg_malloc
= (allocated
+ (mi
.um_malloc
- 1)/2) / mi
.um_malloc
;
4242 * include per-slab overhead
4244 * Each slab in a given cache is the same size, and has the same
4245 * number of chunks in it; we read in the first slab on the
4246 * slab list to get the number of chunks for all slabs. To
4247 * compute the per-slab overhead, we just subtract the chunk usage
4248 * from the slabsize:
4250 * +------------+-------+-------+ ... --+-------+-------+-------+
4251 * |////////////| | | ... | |///////|///////|
4252 * |////color///| chunk | chunk | ... | chunk |/color/|/slab//|
4253 * |////////////| | | ... | |///////|///////|
4254 * +------------+-------+-------+ ... --+-------+-------+-------+
4255 * | \_______chunksize * chunks_____/ |
4256 * \__________________________slabsize__________________________/
4258 * For UMF_HASH caches, there is an additional source of overhead;
4259 * the external umem_slab_t and per-chunk bufctl structures. We
4260 * include those in our per-slab overhead.
4262 * Once we have a number for the per-slab overhead, we estimate
4263 * the actual overhead by treating the malloc()ed buffers as if
4264 * they were densely packed:
4266 * additional overhead = (# mallocs) * (per-slab) / (chunks);
4268 * carefully ordering the multiply before the divide, to avoid
4271 if (mi
.um_malloc
!= 0) {
4273 uintptr_t saddr
= (uintptr_t)c
.cache_nullslab
.slab_next
;
4275 if (mdb_vread(&slab
, sizeof (slab
), saddr
) == -1) {
4276 mdb_warn("unable to read slab at %p\n", saddr
);
4278 long chunks
= slab
.slab_chunks
;
4279 if (chunks
!= 0 && c
.cache_chunksize
!= 0 &&
4280 chunks
<= c
.cache_slabsize
/ c
.cache_chunksize
) {
4283 (c
.cache_chunksize
* chunks
);
4285 if (c
.cache_flags
& UMF_HASH
) {
4286 perslab
+= sizeof (umem_slab_t
) +
4288 ((c
.cache_flags
& UMF_AUDIT
) ?
4289 sizeof (umem_bufctl_audit_t
) :
4290 sizeof (umem_bufctl_t
));
4293 (perslab
* (uintmax_t)mi
.um_malloc
)/chunks
;
4295 mdb_warn("invalid #chunks (%d) in slab %p\n",
4302 overhead_pct
= (1000ULL * overhead
) / allocated
;
4306 mdb_printf("%0?p %6ld %6ld %8ld %8ld %10ld %10ld %3ld.%01ld%%\n",
4307 addr
, c
.cache_bufsize
, maxmalloc
,
4308 mi
.um_malloc
, avg_malloc
, allocated
, overhead
,
4309 overhead_pct
/ 10, overhead_pct
% 10);
4317 if (get_umem_alloc_sizes(&alloc_sizes
, &num
) == -1)
4320 for (idx
= 0; idx
< num
; idx
++) {
4321 if (alloc_sizes
[idx
] == c
.cache_bufsize
)
4323 if (alloc_sizes
[idx
] == 0) {
4324 idx
= num
; /* 0-terminated array */
4330 "cache %p's size (%d) not in umem_alloc_sizes\n",
4331 addr
, c
.cache_bufsize
);
4335 minmalloc
= (idx
== 0)? 0 : alloc_sizes
[idx
- 1];
4336 if (minmalloc
> 0) {
4338 if (minmalloc
> UMEM_SECOND_ALIGN
)
4339 minmalloc
-= sizeof (struct malloc_data
);
4341 minmalloc
-= sizeof (struct malloc_data
);
4346 for (idx
= minmalloc
; idx
<= maxmalloc
; idx
++)
4347 mdb_printf("%d\t%d\n", idx
, mi
.um_bucket
[idx
]);
4350 umem_malloc_print_dist(mi
.um_bucket
, minmalloc
, maxmalloc
,
4351 maxbuckets
, minbucketsize
, geometric
);