dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / mdb / common / modules / libumem / umem.c
blobdf17660d92c93bfd75de4702974ffcf2a4e97c5b
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Copyright 2012 Joyent, Inc. All rights reserved.
28 * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
31 #include "umem.h"
33 #include <sys/vmem_impl_user.h>
34 #include <umem_impl.h>
36 #include <alloca.h>
37 #include <limits.h>
38 #include <mdb/mdb_whatis.h>
39 #include <thr_uberdata.h>
41 #include "misc.h"
42 #include "leaky.h"
43 #include "dist.h"
45 #include "umem_pagesize.h"
47 #define UM_ALLOCATED 0x1
48 #define UM_FREE 0x2
49 #define UM_BUFCTL 0x4
50 #define UM_HASH 0x8
52 int umem_ready;
54 static int umem_stack_depth_warned;
55 static uint32_t umem_max_ncpus;
56 uint32_t umem_stack_depth;
58 size_t umem_pagesize;
60 #define UMEM_READVAR(var) \
61 (umem_readvar(&(var), #var) == -1 && \
62 (mdb_warn("failed to read "#var), 1))
64 int
65 umem_update_variables(void)
67 size_t pagesize;
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) {
74 umem_ready = 0;
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");
85 return (-1);
87 if (UMEM_READVAR(umem_ready))
88 return (-1);
89 if (UMEM_READVAR(umem_stack_depth))
90 return (-1);
91 if (UMEM_READVAR(pagesize))
92 return (-1);
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;
105 return (0);
108 static int
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'");
114 return (WALK_ERR);
118 return (WALK_NEXT);
121 static int
122 umem_ptc_walk_step(mdb_walk_state_t *wsp)
124 uintptr_t this;
125 int rval;
127 if (wsp->walk_layer != NULL) {
128 this = (uintptr_t)((ulwp_t *)wsp->walk_layer)->ul_self +
129 (uintptr_t)wsp->walk_arg;
130 } else {
131 this = wsp->walk_addr + (uintptr_t)wsp->walk_arg;
134 for (;;) {
135 if (mdb_vread(&this, sizeof (void *), this) == -1) {
136 mdb_warn("couldn't read ptc buffer at %p", this);
137 return (WALK_ERR);
140 if (this == (uintptr_t)NULL)
141 break;
143 rval = wsp->walk_callback(this, &this, wsp->walk_cbdata);
145 if (rval != WALK_NEXT)
146 return (rval);
149 return (wsp->walk_layer != NULL ? WALK_NEXT : WALK_DONE);
152 /*ARGSUSED*/
153 static int
154 umem_init_walkers(uintptr_t addr, const umem_cache_t *c, int *sizes)
156 mdb_walker_t w;
157 char descr[64];
158 char name[64];
159 int i;
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))
175 return (WALK_NEXT);
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)
183 break;
186 if (sizes[i] == 0) {
187 mdb_warn("cache %s is cached per-thread, but could not find "
188 "size in umem_alloc_sizes\n", c->cache_name);
189 return (WALK_NEXT);
192 if (i >= NTMEMBASE) {
193 mdb_warn("index for %s (%d) exceeds root slots (%d)\n",
194 c->cache_name, i, NTMEMBASE);
195 return (WALK_NEXT);
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);
203 w.walk_name = name;
204 w.walk_descr = descr;
205 w.walk_init = umem_ptc_walk_init;
206 w.walk_step = umem_ptc_walk_step;
207 w.walk_fini = NULL;
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);
213 return (WALK_NEXT);
216 /*ARGSUSED*/
217 static void
218 umem_statechange_cb(void *arg)
220 static int been_ready = 0;
221 GElf_Sym sym;
222 int *sizes;
224 #ifndef _KMDB
225 leaky_cleanup(1); /* state changes invalidate leaky state */
226 #endif
228 if (umem_update_variables() == -1)
229 return;
231 if (been_ready)
232 return;
234 if (umem_ready != UMEM_READY)
235 return;
237 been_ready = 1;
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'");
246 return;
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'");
253 return;
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;
264 GElf_Sym sym;
265 size_t bufsize;
267 if (UMEM_READVAR(umem_error_begin))
268 return (DCMD_ERR);
270 if (umem_lookup_by_name("umem_error_buffer", &sym) == -1) {
271 mdb_warn("unable to look up umem_error_buffer");
272 return (DCMD_ERR);
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)
280 != bufsize) {
281 mdb_warn("unable to read umem_error_buffer");
282 return (DCMD_ERR);
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);
289 else {
290 umem_error_buffer[(umem_error_begin % bufsize) - 1] = 0;
291 mdb_printf("%s%s\n",
292 &umem_error_buffer[umem_error_begin % bufsize],
293 umem_error_buffer);
296 return (DCMD_OK);
299 static void
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;
304 size_t size;
306 if (pos == (uintptr_t)NULL)
307 return;
309 if (mdb_vread(&my_lh, sizeof (umem_log_header_t), pos) == -1) {
310 mdb_warn("\nunable to read umem_%s_log pointer %p",
311 name, pos);
312 return;
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);
321 else
322 mdb_printf("%s=%d ", name, size);
325 typedef struct umem_debug_flags {
326 const char *udf_name;
327 uint_t udf_flags;
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 },
340 { NULL }
343 /*ARGSUSED*/
345 umem_status(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
347 int umem_logging;
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)");
362 if (umem_ready == 0)
363 return (DCMD_OK);
365 mdb_printf("Concurrency:\t%d\n", umem_max_ncpus);
367 if (UMEM_READVAR(umem_logging))
368 goto err;
369 if (UMEM_READVAR(umem_transaction_log))
370 goto err;
371 if (UMEM_READVAR(umem_content_log))
372 goto err;
373 if (UMEM_READVAR(umem_failure_log))
374 goto err;
375 if (UMEM_READVAR(umem_slab_log))
376 goto err;
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);
383 if (!umem_logging)
384 mdb_printf("(inactive)");
385 mdb_printf("\n");
387 mdb_printf("Message buffer:\n");
388 return (umem_abort_messages());
390 err:
391 mdb_printf("Message buffer:\n");
392 (void) umem_abort_messages();
393 return (DCMD_ERR);
396 typedef struct {
397 uintptr_t ucw_first;
398 uintptr_t ucw_current;
399 } umem_cache_walk_t;
402 umem_cache_walk_init(mdb_walk_state_t *wsp)
404 umem_cache_walk_t *ucw;
405 umem_cache_t c;
406 uintptr_t cp;
407 GElf_Sym sym;
409 if (umem_lookup_by_name("umem_null_cache", &sym) == -1) {
410 mdb_warn("couldn't find umem_null_cache");
411 return (WALK_ERR);
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);
418 return (WALK_ERR);
421 ucw = mdb_alloc(sizeof (umem_cache_walk_t), UM_SLEEP);
423 ucw->ucw_first = cp;
424 ucw->ucw_current = (uintptr_t)c.cache_next;
425 wsp->walk_data = ucw;
427 return (WALK_NEXT);
431 umem_cache_walk_step(mdb_walk_state_t *wsp)
433 umem_cache_walk_t *ucw = wsp->walk_data;
434 umem_cache_t c;
435 int status;
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);
439 return (WALK_DONE);
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)
445 return (WALK_DONE);
447 return (status);
450 void
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));
457 typedef struct {
458 umem_cpu_t *ucw_cpus;
459 uint32_t ucw_current;
460 uint32_t ucw_max;
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'");
472 return (WALK_ERR);
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;
482 return (WALK_NEXT);
486 umem_cpu_walk_step(mdb_walk_state_t *wsp)
488 umem_cpu_t cpu;
489 umem_cpu_walk_state_t *ucw = wsp->walk_data;
491 uintptr_t caddr;
493 if (ucw->ucw_current >= ucw->ucw_max)
494 return (WALK_DONE);
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);
500 return (WALK_ERR);
503 ucw->ucw_current++;
505 return (wsp->walk_callback(caddr, &cpu, wsp->walk_cbdata));
508 void
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");
521 return (WALK_ERR);
524 if (mdb_layered_walk("umem_cpu", wsp) == -1) {
525 mdb_warn("couldn't walk 'umem_cpu'");
526 return (WALK_ERR);
529 wsp->walk_data = (void *)wsp->walk_addr;
531 return (WALK_NEXT);
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;
539 umem_cpu_cache_t cc;
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);
545 return (WALK_ERR);
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;
555 umem_cache_t c;
557 if (caddr == (uintptr_t)NULL) {
558 mdb_warn("umem_slab doesn't support global walks\n");
559 return (WALK_ERR);
562 if (mdb_vread(&c, sizeof (c), caddr) == -1) {
563 mdb_warn("couldn't read umem_cache at %p", caddr);
564 return (WALK_ERR);
567 wsp->walk_data =
568 (void *)(caddr + offsetof(umem_cache_t, cache_nullslab));
569 wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_next;
571 return (WALK_NEXT);
575 umem_slab_walk_partial_init(mdb_walk_state_t *wsp)
577 uintptr_t caddr = wsp->walk_addr;
578 umem_cache_t c;
580 if (caddr == (uintptr_t)NULL) {
581 mdb_warn("umem_slab_partial doesn't support global walks\n");
582 return (WALK_ERR);
585 if (mdb_vread(&c, sizeof (c), caddr) == -1) {
586 mdb_warn("couldn't read umem_cache at %p", caddr);
587 return (WALK_ERR);
590 wsp->walk_data =
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
598 * any.
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;
605 return (WALK_NEXT);
609 umem_slab_walk_step(mdb_walk_state_t *wsp)
611 umem_slab_t s;
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);
616 if (addr == saddr)
617 return (WALK_DONE);
619 if (mdb_vread(&s, sizeof (s), addr) == -1) {
620 mdb_warn("failed to read slab at %p", wsp->walk_addr);
621 return (WALK_ERR);
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);
627 return (WALK_ERR);
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)
638 umem_cache_t c;
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");
643 return (DCMD_ERR);
645 return (DCMD_OK);
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);
654 return (DCMD_ERR);
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);
660 return (DCMD_OK);
663 static int
664 addrcmp(const void *lhs, const void *rhs)
666 uintptr_t p1 = *((uintptr_t *)lhs);
667 uintptr_t p2 = *((uintptr_t *)rhs);
669 if (p1 < p2)
670 return (-1);
671 if (p1 > p2)
672 return (1);
673 return (0);
676 static int
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)
683 return (-1);
685 if (bcp1->bc_timestamp < bcp2->bc_timestamp)
686 return (1);
688 return (0);
691 typedef struct umem_hash_walk {
692 uintptr_t *umhw_table;
693 size_t umhw_nelems;
694 size_t umhw_pos;
695 umem_bufctl_t umhw_cur;
696 } umem_hash_walk_t;
699 umem_hash_walk_init(mdb_walk_state_t *wsp)
701 umem_hash_walk_t *umhw;
702 uintptr_t *hash;
703 umem_cache_t c;
704 uintptr_t haddr, addr = wsp->walk_addr;
705 size_t nelems;
706 size_t hsize;
708 if (addr == (uintptr_t)NULL) {
709 mdb_warn("umem_hash doesn't support global walks\n");
710 return (WALK_ERR);
713 if (mdb_vread(&c, sizeof (c), addr) == -1) {
714 mdb_warn("couldn't read cache at addr %p", addr);
715 return (WALK_ERR);
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;
725 umhw->umhw_pos = 0;
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));
736 return (WALK_ERR);
739 wsp->walk_data = umhw;
741 return (WALK_NEXT);
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++]) !=
753 (uintptr_t)NULL)
754 break;
757 if (addr == (uintptr_t)NULL)
758 return (WALK_DONE);
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);
762 return (WALK_ERR);
765 return (wsp->walk_callback(addr, &umhw->umhw_cur, wsp->walk_cbdata));
768 void
769 umem_hash_walk_fini(mdb_walk_state_t *wsp)
771 umem_hash_walk_t *umhw = wsp->walk_data;
773 if (umhw == NULL)
774 return;
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.
784 static int
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);
788 umem_bufctl_t *bcp;
789 umem_bufctl_t bc;
791 if (mdb_vread(&bcp, sizeof (umem_bufctl_t *), bucket) == -1) {
792 mdb_warn("unable to read hash bucket for %p in cache %p",
793 buf, caddr);
794 return (-1);
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);
801 return (-1);
803 if (bc.bc_addr == buf) {
804 *out = (uintptr_t)bcp;
805 return (0);
807 bcp = bc.bc_next;
810 mdb_warn("unable to find bufctl for %p in cache %p\n", buf, caddr);
811 return (-1);
815 umem_get_magsize(const umem_cache_t *cp)
817 uintptr_t addr = (uintptr_t)cp->cache_magtype;
818 GElf_Sym mt_sym;
819 umem_magtype_t mt;
820 int res;
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))
829 return (res);
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);
838 return (0);
840 if (mdb_vread(&mt, sizeof (mt), addr) == -1) {
841 mdb_warn("unable to read magtype at %a", addr);
842 return (0);
844 return (mt.mt_magsize);
847 /*ARGSUSED*/
848 static int
849 umem_estimate_slab(uintptr_t addr, const umem_slab_t *sp, size_t *est)
851 *est -= (sp->slab_chunks - sp->slab_refcnt);
853 return (WALK_NEXT);
857 * Returns an upper bound on the number of allocated buffers in a given
858 * cache.
860 size_t
861 umem_estimate_allocated(uintptr_t addr, const umem_cache_t *cp)
863 int magsize;
864 size_t cache_est;
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;
876 } else {
877 mdb_warn("cache %p's magazine layer holds more buffers "
878 "than the slab layer.\n", addr);
881 return (cache_est);
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); \
887 goto fail; \
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", \
893 magcnt); \
894 goto fail; \
899 static int
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;
905 int i, cpu;
906 size_t magsize, magmax, magbsize;
907 size_t magcnt = 0;
910 * Read the magtype out of the cache, after verifying the pointer's
911 * correctness.
913 magsize = umem_get_magsize(cp);
914 if (magsize == 0) {
915 *maglistp = NULL;
916 *magcntp = 0;
917 *magmaxp = 0;
918 return (0);
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
931 * crash(1M)).
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",
938 addr, magbsize);
939 return (-1);
942 maglist = mdb_alloc(magmax * sizeof (void *), UM_SLEEP);
943 mp = mdb_alloc(magbsize, UM_SLEEP);
944 if (mp == NULL || maglist == NULL)
945 goto fail;
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);
952 ump = mp->mag_next;
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
962 * and full spares.
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",
979 ccp->cc_prounds));
980 READMAG_ROUNDS(ccp->cc_prounds);
984 dprintf(("magazine layer: %d buffers\n", magcnt));
986 mdb_free(mp, magbsize);
988 *maglistp = maglist;
989 *magcntp = magcnt;
990 *magmaxp = magmax;
992 return (0);
994 fail:
995 if (mp)
996 mdb_free(mp, magbsize);
997 if (maglist)
998 mdb_free(maglist, magmax * sizeof (void *));
1000 return (-1);
1003 typedef struct umem_read_ptc_walk {
1004 void **urpw_buf;
1005 size_t urpw_cnt;
1006 size_t urpw_max;
1007 } umem_read_ptc_walk_t;
1009 /*ARGSUSED*/
1010 static int
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);
1018 if (nmax > 1) {
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;
1030 return (WALK_NEXT);
1033 static int
1034 umem_read_ptc(umem_cache_t *cp,
1035 void ***buflistp, size_t *bufcntp, size_t *bufmaxp)
1037 umem_read_ptc_walk_t urpw;
1038 char walk[60];
1039 int rval;
1041 if (!(cp->cache_flags & UMF_PTC))
1042 return (0);
1044 (void) mdb_snprintf(walk, sizeof (walk), "umem_ptc_%d",
1045 cp->cache_bufsize);
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;
1060 return (rval);
1063 static int
1064 umem_walk_callback(mdb_walk_state_t *wsp, uintptr_t buf)
1066 return (wsp->walk_callback(buf, NULL, wsp->walk_cbdata));
1069 static int
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
1077 * umem_bufctl_t.
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);
1084 return (WALK_ERR);
1088 return (wsp->walk_callback(buf, b, wsp->walk_cbdata));
1091 typedef struct umem_walk {
1092 int umw_type;
1094 uintptr_t umw_addr; /* cache address */
1095 umem_cache_t *umw_cp;
1096 size_t umw_csize;
1099 * magazine layer
1101 void **umw_maglist;
1102 size_t umw_max;
1103 size_t umw_count;
1104 size_t umw_pos;
1107 * slab layer
1109 char *umw_valid; /* to keep track of freed buffers */
1110 char *umw_ubase; /* buffer for slab data */
1111 } umem_walk_t;
1113 static int
1114 umem_walk_init_common(mdb_walk_state_t *wsp, int type)
1116 umem_walk_t *umw;
1117 int csize;
1118 umem_cache_t *cp;
1119 size_t vm_quantum;
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;
1128 type &= ~UM_HASH;
1130 if (addr == (uintptr_t)NULL) {
1131 mdb_warn("umem walk doesn't support global walks\n");
1132 return (WALK_ERR);
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);
1145 goto out2;
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 ||
1160 vm_quantum == 0 ||
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);
1167 goto out2;
1170 dprintf(("buf total is %d\n", cp->cache_buftotal));
1172 if (cp->cache_buftotal == 0) {
1173 mdb_free(cp, csize);
1174 return (WALK_DONE);
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",
1183 cp->cache_flags));
1184 mdb_free(cp, csize);
1185 return (WALK_DONE);
1189 * Read in the contents of the magazine layer
1191 if (umem_read_magazines(cp, addr, &maglist, &magcnt, &magmax) != 0)
1192 goto out2;
1195 * Read in the contents of the per-thread caches, if any
1197 if (umem_read_ptc(cp, &maglist, &magcnt, &magmax) != 0)
1198 goto out2;
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;
1212 umw->umw_cp = cp;
1213 umw->umw_csize = csize;
1214 umw->umw_maglist = maglist;
1215 umw->umw_max = magmax;
1216 umw->umw_count = magcnt;
1217 umw->umw_pos = 0;
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;
1227 } else {
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";
1235 else
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)
1252 umw->umw_valid =
1253 mdb_alloc(slabsize / chunksize, UM_SLEEP);
1257 status = WALK_NEXT;
1259 if (mdb_layered_walk(layered, wsp) == -1) {
1260 mdb_warn("unable to start layered '%s' walk", layered);
1261 status = WALK_ERR;
1264 out1:
1265 if (status == WALK_ERR) {
1266 if (umw->umw_valid)
1267 mdb_free(umw->umw_valid, slabsize / chunksize);
1269 if (umw->umw_ubase)
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;
1281 out2:
1282 if (status == WALK_ERR)
1283 mdb_free(cp, csize);
1285 return (status);
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;
1299 uintptr_t addr;
1300 const umem_slab_t *sp;
1301 const umem_bufctl_t *bcp;
1302 umem_bufctl_t bc;
1304 int chunks;
1305 char *kbase;
1306 void *buf;
1307 int i, ret;
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;
1323 if (magcnt > 0 &&
1324 bsearch(&buf, maglist, magcnt, sizeof (void *),
1325 addrcmp) != NULL)
1326 return (WALK_NEXT);
1328 if (type & UM_BUFCTL)
1329 return (bufctl_walk_callback(cp, wsp, wsp->walk_addr));
1331 return (umem_walk_callback(wsp, (uintptr_t)buf));
1334 ret = WALK_NEXT;
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++) {
1345 buf = maglist[i];
1347 if (type & UM_BUFCTL) {
1348 uintptr_t out;
1350 if (cp->cache_flags & UMF_BUFTAG) {
1351 umem_buftag_t *btp;
1352 umem_buftag_t tag;
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);
1360 continue;
1362 out = (uintptr_t)tag.bt_bufctl;
1363 } else {
1364 if (umem_hash_lookup(cp, addr, buf,
1365 &out) == -1)
1366 continue;
1368 ret = bufctl_walk_callback(cp, wsp, out);
1369 } else {
1370 ret = umem_walk_callback(wsp, (uintptr_t)buf);
1373 if (ret != WALK_NEXT)
1374 return (ret);
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);
1397 return (WALK_ERR);
1401 * Set up the valid map as fully allocated -- we'll punch
1402 * out the freelist.
1404 if (type & UM_ALLOCATED)
1405 (void) memset(valid, 1, chunks);
1406 } else {
1407 valid = NULL;
1408 ubase = NULL;
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++) {
1424 uint_t ndx;
1426 dprintf(("bcp is %p\n", bcp));
1428 if (bcp == NULL) {
1429 if (i == chunks)
1430 break;
1431 mdb_warn(
1432 "slab %p in cache %p freelist too short by %d\n",
1433 sp, addr, chunks - i);
1434 break;
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",
1440 bcp);
1441 break;
1443 buf = bc.bc_addr;
1444 } else {
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);
1460 break;
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
1474 * try to continue.
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;
1481 * clear its entry
1483 valid[ndx] = 0;
1484 } else {
1486 * Report this freed buffer
1488 if (type & UM_BUFCTL) {
1489 ret = bufctl_walk_callback(cp, wsp,
1490 (uintptr_t)bcp);
1491 } else {
1492 ret = umem_walk_callback(wsp, (uintptr_t)buf);
1494 if (ret != WALK_NEXT)
1495 return (ret);
1498 bcp = bc.bc_next;
1501 if (bcp != NULL) {
1502 dprintf(("slab %p in cache %p freelist too long (%p)\n",
1503 sp, addr, bcp));
1507 * If we are walking freed buffers, the loop above handled reporting
1508 * them.
1510 if (type & UM_FREE)
1511 return (WALK_NEXT);
1513 if (type & UM_BUFCTL) {
1514 mdb_warn("impossible situation: small-slab UM_BUFCTL walk for "
1515 "cache %p\n", addr);
1516 return (WALK_ERR);
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;
1526 if (!valid[i])
1527 continue; /* on slab freelist */
1529 if (magcnt > 0 &&
1530 bsearch(&buf, maglist, magcnt, sizeof (void *),
1531 addrcmp) != NULL)
1532 continue; /* in magazine layer */
1534 ret = umem_walk_callback(wsp, (uintptr_t)buf);
1536 return (ret);
1539 void
1540 umem_walk_fini(mdb_walk_state_t *wsp)
1542 umem_walk_t *umw = wsp->walk_data;
1543 uintptr_t chunksize;
1544 uintptr_t slabsize;
1546 if (umw == NULL)
1547 return;
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));
1564 /*ARGSUSED*/
1565 static int
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)
1575 return (WALK_NEXT);
1577 if (mdb_pwalk(wsp->walk_data, wsp->walk_callback,
1578 wsp->walk_cbdata, addr) == -1)
1579 return (WALK_DONE);
1581 return (WALK_NEXT);
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 {
1627 void *bhw_next;
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");
1642 return (WALK_ERR);
1645 if (mdb_vread(&bc, sizeof (bc), wsp->walk_addr) == -1) {
1646 mdb_warn("unable to read bufctl at %p", wsp->walk_addr);
1647 return (WALK_ERR);
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;
1667 else
1668 bhw->bhw_next = (void *)wsp->walk_addr;
1670 wsp->walk_addr = (uintptr_t)bc.bc_addr;
1671 wsp->walk_data = bhw;
1673 return (WALK_NEXT);
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)
1686 return (WALK_DONE);
1688 if (mdb_vread(b, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) {
1689 mdb_warn("unable to read bufctl at %p", bhw->bhw_next);
1690 return (WALK_ERR);
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))
1702 return (WALK_DONE);
1704 bhw->bhw_next = b->bc_lastlog;
1705 bhw->bhw_timestamp = b->bc_timestamp;
1707 return (wsp->walk_callback(addr, b, wsp->walk_cbdata));
1710 void
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;
1722 size_t ulw_size;
1723 size_t ulw_maxndx;
1724 size_t ulw_ndx;
1725 } umem_log_walk_t;
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'");
1742 return (WALK_ERR);
1745 if (lp == (uintptr_t)NULL) {
1746 mdb_warn("log is disabled\n");
1747 return (WALK_ERR);
1750 ulw = mdb_zalloc(sizeof (umem_log_walk_t), UM_SLEEP);
1751 lhp = &ulw->ulw_lh;
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));
1756 return (WALK_ERR);
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));
1768 return (WALK_ERR);
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++) {
1779 /* LINTED align */
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;
1791 return (WALK_NEXT);
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)
1801 return (WALK_DONE);
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));
1809 void
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 {
1821 uintptr_t abb_addr;
1822 hrtime_t abb_ts;
1823 } allocdby_bufctl_t;
1825 typedef struct allocdby_walk {
1826 const char *abw_walk;
1827 uintptr_t abw_thread;
1828 size_t abw_nbufs;
1829 size_t abw_size;
1830 allocdby_bufctl_t *abw_buf;
1831 size_t abw_ndx;
1832 } allocdby_walk_t;
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)
1839 return (WALK_NEXT);
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;
1851 abw->abw_buf = buf;
1854 abw->abw_buf[abw->abw_nbufs].abb_addr = addr;
1855 abw->abw_buf[abw->abw_nbufs].abb_ts = bcp->bc_timestamp;
1856 abw->abw_nbufs++;
1858 return (WALK_NEXT);
1861 /*ARGSUSED*/
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,
1866 abw, addr) == -1) {
1867 mdb_warn("couldn't walk bufctl for cache %p", addr);
1868 return (WALK_DONE);
1871 return (WALK_NEXT);
1874 static int
1875 allocdby_cmp(const allocdby_bufctl_t *lhs, const allocdby_bufctl_t *rhs)
1877 if (lhs->abb_ts < rhs->abb_ts)
1878 return (1);
1879 if (lhs->abb_ts > rhs->abb_ts)
1880 return (-1);
1881 return (0);
1884 static int
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");
1891 return (WALK_ERR);
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 */
1899 abw->abw_buf =
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);
1908 return (WALK_ERR);
1911 qsort(abw->abw_buf, abw->abw_nbufs, sizeof (allocdby_bufctl_t),
1912 (int(*)(const void *, const void *))allocdby_cmp);
1914 return (WALK_NEXT);
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;
1933 uintptr_t addr;
1934 umem_bufctl_audit_t *bcp;
1935 UMEM_LOCAL_BUFCTL_AUDIT(&bcp);
1937 if (abw->abw_ndx == abw->abw_nbufs)
1938 return (WALK_DONE);
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);
1944 return (WALK_DONE);
1947 return (wsp->walk_callback(addr, bcp, wsp->walk_cbdata));
1950 void
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));
1959 /*ARGSUSED*/
1961 allocdby_walk(uintptr_t addr, const umem_bufctl_audit_t *bcp, void *ignored)
1963 char c[MDB_SYM_NAMLEN];
1964 GElf_Sym sym;
1965 int i;
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)
1971 continue;
1972 if (is_umem_sym(c, "umem_"))
1973 continue;
1974 mdb_printf("%s+0x%lx",
1975 c, bcp->bc_stack[i] - (uintptr_t)sym.st_value);
1976 break;
1978 mdb_printf("\n");
1980 return (WALK_NEXT);
1983 static int
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);
1993 return (DCMD_ERR);
1996 return (DCMD_OK);
1999 /*ARGSUSED*/
2001 allocdby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2003 return (allocdby_common(addr, flags, "allocdby"));
2006 /*ARGSUSED*/
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 {
2014 mdb_whatis_t *wi_w;
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;
2019 int wi_slab_found;
2020 uint_t wi_freemem;
2021 } whatis_info_t;
2023 /* call one of our dcmd functions with "-v" and the provided address */
2024 static void
2025 whatis_call_printer(mdb_dcmd_f *dcmd, uintptr_t addr)
2027 mdb_arg_t a;
2028 a.a_type = MDB_TYPE_STRING;
2029 a.a_un.a_str = "-v";
2031 mdb_printf(":\n");
2032 (void) (*dcmd)(addr, DCMD_ADDRSPEC, 1, &a);
2035 static void
2036 whatis_print_umem(whatis_info_t *wi, uintptr_t maddr, uintptr_t addr,
2037 uintptr_t baddr)
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);
2055 return;
2057 mdb_printf("\n");
2060 /*ARGSUSED*/
2061 static int
2062 whatis_walk_umem(uintptr_t addr, void *ignored, whatis_info_t *wi)
2064 mdb_whatis_t *w = wi->wi_w;
2066 uintptr_t cur;
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));
2075 /*ARGSUSED*/
2076 static int
2077 whatis_walk_bufctl(uintptr_t baddr, const umem_bufctl_t *bcp, whatis_info_t *wi)
2079 mdb_whatis_t *w = wi->wi_w;
2081 uintptr_t cur;
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));
2092 static int
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;
2098 uintptr_t cur;
2100 /* We're not interested in anything but alloc and free segments */
2101 if (vs->vs_type != VMEM_ALLOC && vs->vs_type != VMEM_FREE)
2102 return (WALK_NEXT);
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);
2123 else
2124 mdb_printf("\n");
2127 return (WHATIS_WALKRET(w));
2130 static int
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;
2135 wi->wi_vmem = vmem;
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);
2143 return (WALK_NEXT);
2146 return (WHATIS_WALKRET(w));
2149 /*ARGSUSED*/
2150 static int
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++;
2159 return (WALK_DONE);
2161 return (WALK_NEXT);
2164 static int
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;
2169 mdb_walk_cb_t func;
2170 int do_bufctl;
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 */
2177 else
2178 do_bufctl = ((mdb_whatis_flags(w) & WHATIS_BUFCTL) != 0);
2180 if (do_bufctl) {
2181 walk = "bufctl";
2182 freewalk = "freectl";
2183 func = (mdb_walk_cb_t)whatis_walk_bufctl;
2184 } else {
2185 walk = "umem";
2186 freewalk = "freemem";
2187 func = (mdb_walk_cb_t)whatis_walk_umem;
2190 wi->wi_cache = c;
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,
2207 addr) == -1) {
2208 mdb_warn("can't find umem_slab walker");
2209 return (WALK_DONE);
2211 if (wi->wi_slab_found == 0)
2212 return (WALK_NEXT);
2215 wi->wi_freemem = FALSE;
2216 if (mdb_pwalk(walk, func, wi, addr) == -1) {
2217 mdb_warn("can't find %s walker", walk);
2218 return (WALK_DONE);
2221 if (mdb_whatis_done(w))
2222 return (WALK_DONE);
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);
2234 return (WALK_DONE);
2237 return (WHATIS_WALKRET(w));
2240 static int
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))
2245 return (WALK_NEXT);
2247 return (whatis_walk_cache(addr, c, wi));
2250 static int
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)
2254 return (WALK_NEXT);
2256 return (whatis_walk_cache(addr, c, wi));
2259 static int
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))
2264 return (WALK_NEXT);
2266 return (whatis_walk_cache(addr, c, wi));
2269 /*ARGSUSED*/
2270 static int
2271 whatis_run_umem(mdb_whatis_t *w, void *ignored)
2273 whatis_info_t wi;
2275 bzero(&wi, sizeof (wi));
2276 wi.wi_w = w;
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,
2290 &wi) == -1 ||
2291 mdb_walk("umem_cache", (mdb_walk_cb_t)whatis_walk_metadata,
2292 &wi) == -1 ||
2293 mdb_walk("umem_cache", (mdb_walk_cb_t)whatis_walk_notouch,
2294 &wi) == -1) {
2295 mdb_warn("couldn't find umem_cache walker");
2296 return (1);
2298 return (0);
2301 /*ARGSUSED*/
2302 static int
2303 whatis_run_vmem(mdb_whatis_t *w, void *ignored)
2305 whatis_info_t wi;
2307 bzero(&wi, sizeof (wi));
2308 wi.wi_w = w;
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");
2313 return (1);
2315 return (0);
2319 umem_init(void)
2321 mdb_walker_t w = {
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");
2328 return (-1);
2331 if (umem_update_variables() == -1)
2332 return (-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);
2346 return (0);
2349 typedef struct umem_log_cpu {
2350 uintptr_t umc_low;
2351 uintptr_t umc_high;
2352 } umem_log_cpu_t;
2355 umem_log_walk(uintptr_t addr, const umem_bufctl_audit_t *b, umem_log_cpu_t *umc)
2357 int i;
2359 for (i = 0; i < umem_max_ncpus; i++) {
2360 if (addr >= umc[i].umc_low && addr < umc[i].umc_high)
2361 break;
2364 if (i == umem_max_ncpus)
2365 mdb_printf(" ");
2366 else
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);
2372 return (WALK_NEXT);
2375 /*ARGSUSED*/
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;
2383 int i;
2385 if (umem_readvar(&lhp, "umem_transaction_log") == -1) {
2386 mdb_warn("failed to read 'umem_transaction_log'");
2387 return (DCMD_ERR);
2390 if (lhp == (uintptr_t)NULL) {
2391 mdb_warn("no umem transaction log\n");
2392 return (DCMD_ERR);
2395 if (mdb_vread(&lh, sizeof (umem_log_header_t), lhp) == -1) {
2396 mdb_warn("failed to read log header at %p", lhp);
2397 return (DCMD_ERR);
2400 clhp = lhp + ((uintptr_t)&lh.lh_cpu[0] - (uintptr_t)&lh);
2402 umc = mdb_zalloc(sizeof (umem_log_cpu_t) * umem_max_ncpus,
2403 UM_SLEEP | UM_GC);
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",
2408 i, clhp);
2409 return (DCMD_ERR);
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
2426 * log entry.
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);
2434 return (DCMD_ERR);
2437 (void) umem_log_walk(addr, bp, umc);
2439 return (DCMD_OK);
2442 if (mdb_walk("umem_log", (mdb_walk_cb_t)umem_log_walk, umc) == -1) {
2443 mdb_warn("can't find umem log walker");
2444 return (DCMD_ERR);
2447 return (DCMD_OK);
2450 typedef struct bufctl_history_cb {
2451 int bhc_flags;
2452 int bhc_argc;
2453 const mdb_arg_t *bhc_argv;
2454 int bhc_ret;
2455 } bufctl_history_cb_t;
2457 /*ARGSUSED*/
2458 static int
2459 bufctl_history_callback(uintptr_t addr, const void *ign, void *arg)
2461 bufctl_history_cb_t *bhc = arg;
2463 bhc->bhc_ret =
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);
2471 void
2472 bufctl_help(void)
2474 mdb_printf("%s\n",
2475 "Display the contents of umem_bufctl_audit_ts, with optional filtering.\n");
2476 mdb_dec_indent(2);
2477 mdb_printf("%<b>OPTIONS%</b>\n");
2478 mdb_inc_indent(2);
2479 mdb_printf("%s",
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"
2482 " -a addr\n"
2483 " filter out bufctls not involving the buffer at addr\n"
2484 " -c caller\n"
2485 " filter out bufctls without the function/PC in their stack trace\n"
2486 " -e earliest\n"
2487 " filter out bufctls timestamped before earliest\n"
2488 " -l latest\n"
2489 " filter out bufctls timestamped after latest\n"
2490 " -t thread\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;
2503 int i, depth;
2504 char c[MDB_SYM_NAMLEN];
2505 GElf_Sym sym;
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),
2528 UM_SLEEP | UM_GC);
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,
2548 addr) == -1) {
2549 mdb_warn("unable to walk bufctl_history");
2550 return (DCMD_ERR);
2553 if (bhc.bhc_ret == DCMD_OK && !(flags & DCMD_PIPE_OUT))
2554 mdb_printf("\n");
2556 return (bhc.bhc_ret);
2559 if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
2560 if (verbose) {
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");
2565 } else {
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);
2573 return (DCMD_ERR);
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) {
2583 laddr = caller;
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)
2599 break;
2601 if (i == depth)
2602 return (DCMD_OK);
2605 if (thread != (uintptr_t)NULL && (uintptr_t)bcp->bc_thread != thread)
2606 return (DCMD_OK);
2608 if (earliest != 0 && bcp->bc_timestamp < earliest)
2609 return (DCMD_OK);
2611 if (latest != 0 && bcp->bc_timestamp > latest)
2612 return (DCMD_OK);
2614 if (baddr != (uintptr_t)NULL && (uintptr_t)bcp->bc_addr != baddr)
2615 return (DCMD_OK);
2617 if (flags & DCMD_PIPE_OUT) {
2618 mdb_printf("%#r\n", addr);
2619 return (DCMD_OK);
2622 if (verbose) {
2623 mdb_printf(
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);
2629 mdb_inc_indent(17);
2630 for (i = 0; i < depth; i++)
2631 mdb_printf("%a\n", bcp->bc_stack[i]);
2632 mdb_dec_indent(17);
2633 mdb_printf("\n");
2634 } else {
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)
2641 continue;
2642 if (is_umem_sym(c, "umem_"))
2643 continue;
2644 mdb_printf(" %a\n", bcp->bc_stack[i]);
2645 break;
2648 if (i >= depth)
2649 mdb_printf("\n");
2652 return (DCMD_OK);
2655 /*ARGSUSED*/
2657 bufctl_audit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2659 mdb_arg_t a;
2661 if (!(flags & DCMD_ADDRSPEC))
2662 return (DCMD_USAGE);
2664 if (argc != 0)
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 */
2679 } umem_verify_t;
2682 * verify_pattern()
2683 * verify that buf is filled with the pattern pat.
2685 static int64_t
2686 verify_pattern(uint64_t *buf_arg, size_t size, uint64_t pat)
2688 /*LINTED*/
2689 uint64_t *bufend = (uint64_t *)((char *)buf_arg + size);
2690 uint64_t *buf;
2692 for (buf = buf_arg; buf < bufend; buf++)
2693 if (*buf != pat)
2694 return ((uintptr_t)buf - (uintptr_t)buf_arg);
2695 return (-1);
2699 * verify_buftag()
2700 * verify that btp->bt_bxstat == (bcp ^ pat)
2702 static int
2703 verify_buftag(umem_buftag_t *btp, uintptr_t pat)
2705 return (btp->bt_bxstat == ((intptr_t)btp->bt_bufctl ^ pat) ? 0 : -1);
2709 * verify_free()
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.
2713 /*ARGSUSED1*/
2714 static int
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;
2724 /*LINTED*/
2725 buftagp = UMEM_BUFTAG(cp, buf);
2728 * Read the buffer to check.
2730 if (mdb_vread(buf, umv->umv_size, addr) == -1) {
2731 if (!besilent)
2732 mdb_warn("couldn't read %p", addr);
2733 return (WALK_NEXT);
2736 if ((corrupt = verify_pattern(buf, cp->cache_verify,
2737 UMEM_FREE_PATTERN)) >= 0) {
2738 if (!besilent)
2739 mdb_printf("buffer %p (free) seems corrupted, at %p\n",
2740 addr, (uintptr_t)addr + corrupt);
2741 goto corrupt;
2744 if ((cp->cache_flags & UMF_HASH) &&
2745 buftagp->bt_redzone != UMEM_REDZONE_PATTERN) {
2746 if (!besilent)
2747 mdb_printf("buffer %p (free) seems to "
2748 "have a corrupt redzone pattern\n", addr);
2749 goto corrupt;
2753 * confirm bufctl pointer integrity.
2755 if (verify_buftag(buftagp, UMEM_BUFTAG_FREE) == -1) {
2756 if (!besilent)
2757 mdb_printf("buffer %p (free) has a corrupt "
2758 "buftag\n", addr);
2759 goto corrupt;
2762 return (WALK_NEXT);
2763 corrupt:
2764 umv->umv_corruption++;
2765 return (WALK_NEXT);
2769 * verify_alloc()
2770 * Verify that the buftag of an allocated buffer makes sense with respect
2771 * to the buffer.
2773 /*ARGSUSED1*/
2774 static int
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 */
2780 /*LINTED*/
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) {
2791 if (!besilent)
2792 mdb_warn("couldn't read %p", addr);
2793 return (WALK_NEXT);
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)
2812 looks_ok = 1;
2813 else if (!UMEM_SIZE_VALID(ip[1]))
2814 size_ok = 0;
2815 else if (bp[UMEM_SIZE_DECODE(ip[1])] == UMEM_REDZONE_BYTE)
2816 looks_ok = 1;
2817 else
2818 size_ok = 0;
2820 if (!size_ok) {
2821 if (!besilent)
2822 mdb_printf("buffer %p (allocated) has a corrupt "
2823 "redzone size encoding\n", addr);
2824 goto corrupt;
2827 if (!looks_ok) {
2828 if (!besilent)
2829 mdb_printf("buffer %p (allocated) has a corrupt "
2830 "redzone signature\n", addr);
2831 goto corrupt;
2834 if (verify_buftag(buftagp, UMEM_BUFTAG_ALLOC) == -1) {
2835 if (!besilent)
2836 mdb_printf("buffer %p (allocated) has a "
2837 "corrupt buftag\n", addr);
2838 goto corrupt;
2841 return (WALK_NEXT);
2842 corrupt:
2843 umv->umv_corruption++;
2844 return (WALK_NEXT);
2847 /*ARGSUSED2*/
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;
2853 umem_verify_t umv;
2855 if (mdb_vread(&umv.umv_cache, sizeof (umv.umv_cache),
2856 addr) == -1) {
2857 mdb_warn("couldn't read umem_cache %p", addr);
2858 return (DCMD_ERR);
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)) {
2867 check_alloc = 1;
2868 if (umv.umv_cache.cache_flags & UMF_DEADBEEF)
2869 check_free = 1;
2870 } else {
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);
2876 return (DCMD_ERR);
2879 if (flags & DCMD_LOOP) {
2881 * table mode, don't print out every corrupt buffer
2883 umv.umv_besilent = 1;
2884 } else {
2885 mdb_printf("Summary for cache '%s'\n",
2886 umv.umv_cache.cache_name);
2887 mdb_inc_indent(2);
2888 umv.umv_besilent = 0;
2891 if (check_alloc)
2892 (void) mdb_pwalk("umem", verify_alloc, &umv, addr);
2893 if (check_free)
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",
2899 UMEM_CACHE_NAMELEN,
2900 umv.umv_cache.cache_name, addr);
2901 } else {
2902 char *s = ""; /* optional s in "buffer[s]" */
2903 if (umv.umv_corruption > 1)
2904 s = "s";
2906 mdb_printf("%-*s %?p %d corrupt buffer%s\n",
2907 UMEM_CACHE_NAMELEN,
2908 umv.umv_cache.cache_name, addr,
2909 umv.umv_corruption, s);
2911 } else {
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");
2920 mdb_dec_indent(2);
2922 } else {
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));
2933 return (DCMD_OK);
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;
2941 uintptr_t vn_addr;
2942 int vn_marked;
2943 vmem_t vn_vmem;
2944 } vmem_node_t;
2946 typedef struct vmem_walk {
2947 vmem_node_t *vw_root;
2948 vmem_node_t *vw_current;
2949 } vmem_walk_t;
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;
2956 vmem_walk_t *vw;
2958 if (umem_readvar(&vaddr, "vmem_list") == -1) {
2959 mdb_warn("couldn't read 'vmem_list'");
2960 return (WALK_ERR);
2963 while (vaddr != (uintptr_t)NULL) {
2964 vp = mdb_zalloc(sizeof (vmem_node_t), UM_SLEEP);
2965 vp->vn_addr = vaddr;
2966 vp->vn_next = head;
2967 head = vp;
2969 if (vaddr == wsp->walk_addr)
2970 current = vp;
2972 if (mdb_vread(&vp->vn_vmem, sizeof (vmem_t), vaddr) == -1) {
2973 mdb_warn("couldn't read vmem_t at %p", vaddr);
2974 goto err;
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) ==
2983 (uintptr_t)NULL) {
2984 vp->vn_sibling = root;
2985 root = vp;
2986 continue;
2989 for (parent = head; parent != NULL; parent = parent->vn_next) {
2990 if (parent->vn_addr != paddr)
2991 continue;
2992 vp->vn_sibling = parent->vn_children;
2993 parent->vn_children = vp;
2994 vp->vn_parent = parent;
2995 break;
2998 if (parent == NULL) {
2999 mdb_warn("couldn't find %p's parent (%p)\n",
3000 vp->vn_addr, paddr);
3001 goto err;
3005 vw = mdb_zalloc(sizeof (vmem_walk_t), UM_SLEEP);
3006 vw->vw_root = root;
3008 if (current != NULL)
3009 vw->vw_current = current;
3010 else
3011 vw->vw_current = root;
3013 wsp->walk_data = vw;
3014 return (WALK_NEXT);
3015 err:
3016 for (vp = head; head != NULL; vp = head) {
3017 head = vp->vn_next;
3018 mdb_free(vp, sizeof (vmem_node_t));
3021 return (WALK_ERR);
3025 vmem_walk_step(mdb_walk_state_t *wsp)
3027 vmem_walk_t *vw = wsp->walk_data;
3028 vmem_node_t *vp;
3029 int rval;
3031 if ((vp = vw->vw_current) == NULL)
3032 return (WALK_DONE);
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;
3038 return (rval);
3041 do {
3042 vw->vw_current = vp->vn_sibling;
3043 vp = vp->vn_parent;
3044 } while (vw->vw_current == NULL && vp != NULL);
3046 return (rval);
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;
3060 int rval;
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)
3073 vp = vp->vn_parent;
3074 else {
3076 * We have neither a parent, nor a sibling, and we
3077 * have already been visited; we're done.
3079 return (WALK_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;
3089 vp->vn_marked = 1;
3090 vw->vw_current = vp;
3091 rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata);
3093 return (rval);
3096 void
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;
3101 int done;
3103 if (root == NULL)
3104 return;
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));
3113 if (done) {
3114 mdb_free(vw, sizeof (vmem_walk_t));
3115 } else {
3116 vmem_walk_fini(wsp);
3120 typedef struct vmem_seg_walk {
3121 uint8_t vsw_type;
3122 uintptr_t vsw_start;
3123 uintptr_t vsw_current;
3124 } vmem_seg_walk_t;
3126 /*ARGSUSED*/
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);
3134 return (WALK_ERR);
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;
3143 return (WALK_NEXT);
3147 * vmem segments can't have type 0 (this should be added to vmem_impl.h).
3149 #define VMEM_NONE 0
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)
3178 vmem_seg_t seg;
3179 vmem_seg_walk_t *vsw = wsp->walk_data;
3180 uintptr_t addr = vsw->vsw_current;
3181 static size_t seg_size = 0;
3182 int rval;
3184 if (!seg_size) {
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);
3196 return (WALK_ERR);
3199 vsw->vsw_current = (uintptr_t)seg.vs_anext;
3200 if (vsw->vsw_type != VMEM_NONE && seg.vs_type != vsw->vsw_type) {
3201 rval = WALK_NEXT;
3202 } else {
3203 rval = wsp->walk_callback(addr, &seg, wsp->walk_cbdata);
3206 if (vsw->vsw_current == vsw->vsw_start)
3207 return (WALK_DONE);
3209 return (rval);
3212 void
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)
3225 vmem_t v, parent;
3226 uintptr_t paddr;
3227 int ident = 0;
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");
3233 return (DCMD_ERR);
3235 return (DCMD_OK);
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);
3245 return (DCMD_ERR);
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);
3252 ident = 0;
3253 break;
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);
3265 return (DCMD_OK);
3268 void
3269 vmem_seg_help(void)
3271 mdb_printf("%s\n",
3272 "Display the contents of vmem_seg_ts, with optional filtering.\n"
3273 "\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"
3276 "information.\n");
3277 mdb_dec_indent(2);
3278 mdb_printf("%<b>OPTIONS%</b>\n");
3279 mdb_inc_indent(2);
3280 mdb_printf("%s",
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"
3283 " -c caller\n"
3284 " filter out segments without the function/PC in their stack trace\n"
3285 " -e earliest\n"
3286 " filter out segments timestamped before earliest\n"
3287 " -l latest\n"
3288 " filter out segments timestamped after latest\n"
3289 " -m minsize\n"
3290 " filer out segments smaller than minsize\n"
3291 " -M maxsize\n"
3292 " filer out segments larger than maxsize\n"
3293 " -t thread\n"
3294 " filter out segments not involving thread\n"
3295 " -T type\n"
3296 " filter out segments not of type 'type'\n"
3297 " type is one of: ALLOC/FREE/SPAN/ROTOR/WALKER\n");
3301 /*ARGSUSED*/
3303 vmem_seg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3305 vmem_seg_t vs;
3306 uintptr_t *stk = vs.vs_stack;
3307 uintptr_t sz;
3308 uint8_t t;
3309 const char *type = NULL;
3310 GElf_Sym sym;
3311 char c[MDB_SYM_NAMLEN];
3312 int no_debug;
3313 int i;
3314 int depth;
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;
3322 uint_t size = 0;
3323 uint_t verbose = 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,
3338 NULL) != argc)
3339 return (DCMD_USAGE);
3341 if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
3342 if (verbose) {
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", "");
3347 } else {
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);
3355 return (DCMD_ERR);
3358 if (type != NULL) {
3359 if (strcmp(type, "ALLC") == 0 || strcmp(type, "ALLOC") == 0)
3360 t = VMEM_ALLOC;
3361 else if (strcmp(type, "FREE") == 0)
3362 t = VMEM_FREE;
3363 else if (strcmp(type, "SPAN") == 0)
3364 t = VMEM_SPAN;
3365 else if (strcmp(type, "ROTR") == 0 ||
3366 strcmp(type, "ROTOR") == 0)
3367 t = VMEM_ROTOR;
3368 else if (strcmp(type, "WLKR") == 0 ||
3369 strcmp(type, "WALKER") == 0)
3370 t = VMEM_WALKER;
3371 else {
3372 mdb_warn("\"%s\" is not a recognized vmem_seg type\n",
3373 type);
3374 return (DCMD_ERR);
3377 if (vs.vs_type != t)
3378 return (DCMD_OK);
3381 sz = vs.vs_end - vs.vs_start;
3383 if (minsize != 0 && sz < minsize)
3384 return (DCMD_OK);
3386 if (maxsize != 0 && sz > maxsize)
3387 return (DCMD_OK);
3389 t = vs.vs_type;
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);
3398 if (no_debug) {
3399 if (caller != (uintptr_t)NULL || thread != (uintptr_t)NULL ||
3400 earliest != 0 || latest != 0)
3401 return (DCMD_OK); /* not enough info */
3402 } else {
3403 if (caller != (uintptr_t)NULL) {
3404 laddr = caller;
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)
3421 break;
3423 if (i == depth)
3424 return (DCMD_OK);
3427 if (thread != (uintptr_t)NULL &&
3428 (uintptr_t)vs.vs_thread != thread)
3429 return (DCMD_OK);
3431 if (earliest != 0 && vs.vs_timestamp < earliest)
3432 return (DCMD_OK);
3434 if (latest != 0 && vs.vs_timestamp > latest)
3435 return (DCMD_OK);
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" :
3443 "????");
3445 if (flags & DCMD_PIPE_OUT) {
3446 mdb_printf("%#r\n", addr);
3447 return (DCMD_OK);
3450 if (verbose) {
3451 mdb_printf("%<b>%16p%</b> %4s %16p %16p %16d\n",
3452 addr, type, vs.vs_start, vs.vs_end, sz);
3454 if (no_debug)
3455 return (DCMD_OK);
3457 mdb_printf("%16s %4s %16d %16llx\n",
3458 "", "", vs.vs_thread, vs.vs_timestamp);
3460 mdb_inc_indent(17);
3461 for (i = 0; i < depth; i++) {
3462 mdb_printf("%a\n", stk[i]);
3464 mdb_dec_indent(17);
3465 mdb_printf("\n");
3466 } else {
3467 mdb_printf("%0?p %4s %0?p %0?p", addr, type,
3468 vs.vs_start, size? sz : vs.vs_end);
3470 if (no_debug) {
3471 mdb_printf("\n");
3472 return (DCMD_OK);
3475 for (i = 0; i < depth; i++) {
3476 if (mdb_lookup_by_addr(stk[i], MDB_SYM_FUZZY,
3477 c, sizeof (c), &sym) == -1)
3478 continue;
3479 if (is_umem_sym(c, "vmem_"))
3480 continue;
3481 break;
3483 mdb_printf(" %a\n", stk[i]);
3485 return (DCMD_OK);
3488 /*ARGSUSED*/
3489 static int
3490 showbc(uintptr_t addr, const umem_bufctl_audit_t *bcp, hrtime_t *newest)
3492 char name[UMEM_CACHE_NAMELEN + 1];
3493 hrtime_t delta;
3494 int i, depth;
3496 if (bcp->bc_timestamp == 0)
3497 return (WALK_DONE);
3499 if (*newest == 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]);
3515 return (WALK_NEXT);
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);
3527 if (argc > 0) {
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";
3534 else
3535 return (DCMD_USAGE);
3538 if (umem_readvar(&addr, logname) == -1) {
3539 mdb_warn("failed to read %s log header pointer");
3540 return (DCMD_ERR);
3543 if (mdb_pwalk("umem_log", (mdb_walk_cb_t)showbc, &newest, addr) == -1) {
3544 mdb_warn("failed to walk umem log");
3545 return (DCMD_ERR);
3548 return (DCMD_OK);
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 */
3563 } umclist_t;
3565 static int
3566 umc_add(uintptr_t addr, const umem_cache_t *cp, umclist_t *umc)
3568 void *p;
3569 int s;
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;
3585 umc->umc_size = s;
3588 umc->umc_caches[umc->umc_nelems++] = addr;
3589 return (umc->umc_name ? WALK_DONE : WALK_NEXT);
3592 return (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 */
3612 } umowner_t;
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 */
3620 } umusers_t;
3622 static void
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;
3646 umu->umu_size = s;
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] -
3681 bcp->bc_stack[i];
3684 if (difference == 0) {
3685 umo->umo_total_size += size;
3686 umo->umo_num++;
3687 return;
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;
3701 umo->umo_num = 1;
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.
3714 /*ARGSUSED*/
3715 static int
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);
3721 return (WALK_NEXT);
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.
3728 static int
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);
3741 return (WALK_NEXT);
3745 * We sort our results by allocation size before printing them.
3747 static int
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.
3762 /*ARGSUSED*/
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;
3775 int i, oelems;
3777 umclist_t umc;
3778 umusers_t umu;
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);
3802 return (DCMD_ERR);
3805 do_all_caches = 0;
3806 argv++;
3807 argc--;
3810 if (opt_e)
3811 mem_threshold = cnt_threshold = 0;
3813 if (opt_f)
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];
3823 umem_cache_t c;
3825 if (mdb_vread(&c, sizeof (c), cp) == -1) {
3826 mdb_warn("failed to read cache at %p", cp);
3827 continue;
3830 if (!(c.cache_flags & UMF_AUDIT)) {
3831 if (!do_all_caches) {
3832 mdb_warn("UMF_AUDIT is not enabled for %s\n",
3833 c.cache_name);
3835 continue;
3838 umu.umu_cache = &c;
3839 (void) mdb_pwalk("bufctl", callback, &umu, cp);
3840 audited_caches++;
3843 if (audited_caches == 0 && do_all_caches) {
3844 mdb_warn("UMF_AUDIT is not enabled for any caches\n");
3845 return (DCMD_ERR);
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)
3854 continue;
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]);
3861 return (DCMD_OK);
3864 struct malloc_data {
3865 uint32_t malloc_size;
3866 uint32_t malloc_stat; /* == UMEM_MALLOC_ENCODE(state, malloc_size) */
3869 #ifdef _LP64
3870 #define UMI_MAX_BUCKET (UMEM_MAXBUF - 2*sizeof (struct malloc_data))
3871 #else
3872 #define UMI_MAX_BUCKET (UMEM_MAXBUF - sizeof (struct malloc_data))
3873 #endif
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;
3883 uint_t *um_bucket;
3884 } umem_malloc_info_t;
3886 static void
3887 umem_malloc_print_dist(uint_t *um_bucket, size_t minmalloc, size_t maxmalloc,
3888 size_t maxbuckets, size_t minbucketsize, int geometric)
3890 uint64_t um_malloc;
3891 int minb = -1;
3892 int maxb = -1;
3893 int buckets;
3894 int nbucks;
3895 int i;
3896 int b;
3897 const int *distarray;
3899 minb = (int)minmalloc;
3900 maxb = (int)maxmalloc;
3902 nbucks = buckets = maxb - minb + 1;
3904 um_malloc = 0;
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);
3913 if (buckets == 0) {
3914 buckets = 1;
3915 minbucketsize = nbucks;
3919 if (geometric)
3920 distarray = dist_geometric(buckets, minb, maxb, minbucketsize);
3921 else
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);
3928 mdb_printf("\n");
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).
3971 /*ARGSUSED*/
3972 static int
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);
3978 size_t mallocsz;
3980 ump->um_total++;
3982 #ifdef _LP64
3983 if (ump->um_cp->cache_bufsize > UMEM_SECOND_ALIGN) {
3984 m_addr += overhead;
3985 overhead += sizeof (md);
3987 #endif
3989 if (mdb_vread(&md, sizeof (md), m_addr) == -1) {
3990 mdb_warn("unable to read malloc header at %p", m_addr);
3991 return (WALK_NEXT);
3994 switch (UMEM_MALLOC_DECODE(md.malloc_stat, md.malloc_size)) {
3995 case MALLOC_MAGIC:
3996 #ifdef _LP64
3997 case MALLOC_SECOND_MAGIC:
3998 #endif
3999 mallocsz = md.malloc_size - overhead;
4001 ump->um_malloc++;
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]++;
4012 break;
4013 default:
4014 break;
4017 return (WALK_NEXT);
4021 get_umem_alloc_sizes(int **out, size_t *out_num)
4023 GElf_Sym sym;
4025 if (umem_lookup_by_name("umem_alloc_sizes", &sym) == -1) {
4026 mdb_warn("unable to look up umem_alloc_sizes");
4027 return (-1);
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);
4035 *out = NULL;
4036 return (-1);
4039 return (0);
4043 static int
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)
4047 return (WALK_NEXT);
4049 ump->um_cp = cp;
4051 if (mdb_pwalk("umem", (mdb_walk_cb_t)um_umem_buffer_cb, ump, addr) ==
4052 -1) {
4053 mdb_warn("can't walk 'umem' for cache %p", addr);
4054 return (WALK_ERR);
4057 return (WALK_NEXT);
4060 void
4061 umem_malloc_dist_help(void)
4063 mdb_printf("%s\n",
4064 "report distribution of outstanding malloc()s");
4065 mdb_dec_indent(2);
4066 mdb_printf("%<b>OPTIONS%</b>\n");
4067 mdb_inc_indent(2);
4068 mdb_printf("%s",
4069 " -b maxbins\n"
4070 " Use at most maxbins bins for the data\n"
4071 " -B minbinsize\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");
4077 /*ARGSUSED*/
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;
4083 uint_t dump = 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,
4098 0) != argc)
4099 return (DCMD_USAGE);
4101 bzero(&mi, sizeof (mi));
4102 mi.um_bucket = mdb_zalloc((UMI_MAX_BUCKET + 1) * sizeof (*mi.um_bucket),
4103 UM_SLEEP | UM_GC);
4105 if (mdb_walk("umem_cache", (mdb_walk_cb_t)um_umem_cache_cb,
4106 &mi) == -1) {
4107 mdb_warn("unable to walk 'umem_cache'");
4108 return (DCMD_ERR);
4111 if (dump) {
4112 int i;
4113 for (i = minalloc; i <= maxalloc; i++)
4114 mdb_printf("%d\t%d\n", i, mi.um_bucket[i]);
4116 return (DCMD_OK);
4119 umem_malloc_print_dist(mi.um_bucket, minalloc, maxalloc,
4120 maxbuckets, minbucketsize, geometric);
4122 return (DCMD_OK);
4125 void
4126 umem_malloc_info_help(void)
4128 mdb_printf("%s\n",
4129 "report information about malloc()s by cache. ");
4130 mdb_dec_indent(2);
4131 mdb_printf("%<b>OPTIONS%</b>\n");
4132 mdb_inc_indent(2);
4133 mdb_printf("%s",
4134 " -b maxbins\n"
4135 " Use at most maxbins bins for the data\n"
4136 " -B minbinsize\n"
4137 " Make the bins at least minbinsize bytes apart\n"
4138 " -d dump the raw distribution data without binning\n"
4139 #ifndef _KMDB
4140 " -g use geometric binning instead of linear binning\n"
4141 #endif
4142 "");
4145 umem_malloc_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
4147 umem_cache_t c;
4148 umem_malloc_info_t mi;
4150 int skip = 0;
4152 size_t maxmalloc;
4153 size_t overhead;
4154 size_t allocated;
4155 size_t avg_malloc;
4156 size_t overhead_pct; /* 1000 * overhead_percent */
4158 uint_t verbose = 0;
4159 uint_t dump = 0;
4160 uint_t geometric = 0;
4161 size_t maxbuckets = 0;
4162 size_t minbucketsize = 0;
4164 int *alloc_sizes;
4165 int idx;
4166 size_t num;
4167 size_t minmalloc;
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,
4174 0) != argc)
4175 return (DCMD_USAGE);
4177 if (dump || geometric || (maxbuckets != 0) || (minbucketsize != 0))
4178 verbose = 1;
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");
4184 return (DCMD_ERR);
4186 return (DCMD_OK);
4189 if (!mdb_vread(&c, sizeof (c), addr)) {
4190 mdb_warn("unable to read cache at %p", addr);
4191 return (DCMD_ERR);
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);
4198 skip = 1;
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");
4210 if (skip)
4211 return (DCMD_OK);
4213 maxmalloc = c.cache_bufsize - sizeof (struct malloc_data);
4214 #ifdef _LP64
4215 if (c.cache_bufsize > UMEM_SECOND_ALIGN)
4216 maxmalloc -= sizeof (struct malloc_data);
4217 #endif
4219 bzero(&mi, sizeof (mi));
4220 mi.um_cp = &c;
4221 if (verbose)
4222 mi.um_bucket =
4223 mdb_zalloc((UMI_MAX_BUCKET + 1) * sizeof (*mi.um_bucket),
4224 UM_SLEEP | UM_GC);
4226 if (mdb_pwalk("umem", (mdb_walk_cb_t)um_umem_buffer_cb, &mi, addr) ==
4227 -1) {
4228 mdb_warn("can't walk 'umem'");
4229 return (DCMD_ERR);
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;
4238 else
4239 avg_malloc = 0;
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
4269 * round-off error.
4271 if (mi.um_malloc != 0) {
4272 umem_slab_t slab;
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);
4277 } else {
4278 long chunks = slab.slab_chunks;
4279 if (chunks != 0 && c.cache_chunksize != 0 &&
4280 chunks <= c.cache_slabsize / c.cache_chunksize) {
4281 uintmax_t perslab =
4282 c.cache_slabsize -
4283 (c.cache_chunksize * chunks);
4285 if (c.cache_flags & UMF_HASH) {
4286 perslab += sizeof (umem_slab_t) +
4287 chunks *
4288 ((c.cache_flags & UMF_AUDIT) ?
4289 sizeof (umem_bufctl_audit_t) :
4290 sizeof (umem_bufctl_t));
4292 overhead +=
4293 (perslab * (uintmax_t)mi.um_malloc)/chunks;
4294 } else {
4295 mdb_warn("invalid #chunks (%d) in slab %p\n",
4296 chunks, saddr);
4301 if (allocated != 0)
4302 overhead_pct = (1000ULL * overhead) / allocated;
4303 else
4304 overhead_pct = 0;
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);
4311 if (!verbose)
4312 return (DCMD_OK);
4314 if (!dump)
4315 mdb_printf("\n");
4317 if (get_umem_alloc_sizes(&alloc_sizes, &num) == -1)
4318 return (DCMD_ERR);
4320 for (idx = 0; idx < num; idx++) {
4321 if (alloc_sizes[idx] == c.cache_bufsize)
4322 break;
4323 if (alloc_sizes[idx] == 0) {
4324 idx = num; /* 0-terminated array */
4325 break;
4328 if (idx == num) {
4329 mdb_warn(
4330 "cache %p's size (%d) not in umem_alloc_sizes\n",
4331 addr, c.cache_bufsize);
4332 return (DCMD_ERR);
4335 minmalloc = (idx == 0)? 0 : alloc_sizes[idx - 1];
4336 if (minmalloc > 0) {
4337 #ifdef _LP64
4338 if (minmalloc > UMEM_SECOND_ALIGN)
4339 minmalloc -= sizeof (struct malloc_data);
4340 #endif
4341 minmalloc -= sizeof (struct malloc_data);
4342 minmalloc += 1;
4345 if (dump) {
4346 for (idx = minmalloc; idx <= maxmalloc; idx++)
4347 mdb_printf("%d\t%d\n", idx, mi.um_bucket[idx]);
4348 mdb_printf("\n");
4349 } else {
4350 umem_malloc_print_dist(mi.um_bucket, minmalloc, maxmalloc,
4351 maxbuckets, minbucketsize, geometric);
4354 return (DCMD_OK);