Home | History | Annotate | Download | only in libumem
      1 /*
      2  * CDDL HEADER START
      3  *
      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.
      7  *
      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.
     12  *
     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]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include "umem.h"
     27 
     28 #include <sys/vmem_impl_user.h>
     29 #include <umem_impl.h>
     30 
     31 #include <alloca.h>
     32 #include <limits.h>
     33 #include <mdb/mdb_whatis.h>
     34 
     35 #include "misc.h"
     36 #include "leaky.h"
     37 #include "dist.h"
     38 
     39 #include "umem_pagesize.h"
     40 
     41 #define	UM_ALLOCATED		0x1
     42 #define	UM_FREE			0x2
     43 #define	UM_BUFCTL		0x4
     44 #define	UM_HASH			0x8
     45 
     46 int umem_ready;
     47 
     48 static int umem_stack_depth_warned;
     49 static uint32_t umem_max_ncpus;
     50 uint32_t umem_stack_depth;
     51 
     52 size_t umem_pagesize;
     53 
     54 #define	UMEM_READVAR(var)				\
     55 	(umem_readvar(&(var), #var) == -1 &&		\
     56 	    (mdb_warn("failed to read "#var), 1))
     57 
     58 int
     59 umem_update_variables(void)
     60 {
     61 	size_t pagesize;
     62 
     63 	/*
     64 	 * Figure out which type of umem is being used; if it's not there
     65 	 * yet, succeed quietly.
     66 	 */
     67 	if (umem_set_standalone() == -1) {
     68 		umem_ready = 0;
     69 		return (0);		/* umem not there yet */
     70 	}
     71 
     72 	/*
     73 	 * Solaris 9 used a different name for umem_max_ncpus.  It's
     74 	 * cheap backwards compatibility to check for both names.
     75 	 */
     76 	if (umem_readvar(&umem_max_ncpus, "umem_max_ncpus") == -1 &&
     77 	    umem_readvar(&umem_max_ncpus, "max_ncpus") == -1) {
     78 		mdb_warn("unable to read umem_max_ncpus or max_ncpus");
     79 		return (-1);
     80 	}
     81 	if (UMEM_READVAR(umem_ready))
     82 		return (-1);
     83 	if (UMEM_READVAR(umem_stack_depth))
     84 		return (-1);
     85 	if (UMEM_READVAR(pagesize))
     86 		return (-1);
     87 
     88 	if (umem_stack_depth > UMEM_MAX_STACK_DEPTH) {
     89 		if (umem_stack_depth_warned == 0) {
     90 			mdb_warn("umem_stack_depth corrupted (%d > %d)\n",
     91 			    umem_stack_depth, UMEM_MAX_STACK_DEPTH);
     92 			umem_stack_depth_warned = 1;
     93 		}
     94 		umem_stack_depth = 0;
     95 	}
     96 
     97 	umem_pagesize = pagesize;
     98 
     99 	return (0);
    100 }
    101 
    102 /*ARGSUSED*/
    103 static int
    104 umem_init_walkers(uintptr_t addr, const umem_cache_t *c, void *ignored)
    105 {
    106 	mdb_walker_t w;
    107 	char descr[64];
    108 
    109 	(void) mdb_snprintf(descr, sizeof (descr),
    110 	    "walk the %s cache", c->cache_name);
    111 
    112 	w.walk_name = c->cache_name;
    113 	w.walk_descr = descr;
    114 	w.walk_init = umem_walk_init;
    115 	w.walk_step = umem_walk_step;
    116 	w.walk_fini = umem_walk_fini;
    117 	w.walk_init_arg = (void *)addr;
    118 
    119 	if (mdb_add_walker(&w) == -1)
    120 		mdb_warn("failed to add %s walker", c->cache_name);
    121 
    122 	return (WALK_NEXT);
    123 }
    124 
    125 /*ARGSUSED*/
    126 static void
    127 umem_statechange_cb(void *arg)
    128 {
    129 	static int been_ready = 0;
    130 
    131 #ifndef _KMDB
    132 	leaky_cleanup(1);	/* state changes invalidate leaky state */
    133 #endif
    134 
    135 	if (umem_update_variables() == -1)
    136 		return;
    137 
    138 	if (been_ready)
    139 		return;
    140 
    141 	if (umem_ready != UMEM_READY)
    142 		return;
    143 
    144 	been_ready = 1;
    145 	(void) mdb_walk("umem_cache", (mdb_walk_cb_t)umem_init_walkers, NULL);
    146 }
    147 
    148 int
    149 umem_abort_messages(void)
    150 {
    151 	char *umem_error_buffer;
    152 	uint_t umem_error_begin;
    153 	GElf_Sym sym;
    154 	size_t bufsize;
    155 
    156 	if (UMEM_READVAR(umem_error_begin))
    157 		return (DCMD_ERR);
    158 
    159 	if (umem_lookup_by_name("umem_error_buffer", &sym) == -1) {
    160 		mdb_warn("unable to look up umem_error_buffer");
    161 		return (DCMD_ERR);
    162 	}
    163 
    164 	bufsize = (size_t)sym.st_size;
    165 
    166 	umem_error_buffer = mdb_alloc(bufsize+1, UM_SLEEP | UM_GC);
    167 
    168 	if (mdb_vread(umem_error_buffer, bufsize, (uintptr_t)sym.st_value)
    169 	    != bufsize) {
    170 		mdb_warn("unable to read umem_error_buffer");
    171 		return (DCMD_ERR);
    172 	}
    173 	/* put a zero after the end of the buffer to simplify printing */
    174 	umem_error_buffer[bufsize] = 0;
    175 
    176 	if ((umem_error_begin % bufsize) == 0)
    177 		mdb_printf("%s\n", umem_error_buffer);
    178 	else {
    179 		umem_error_buffer[(umem_error_begin % bufsize) - 1] = 0;
    180 		mdb_printf("%s%s\n",
    181 		    &umem_error_buffer[umem_error_begin % bufsize],
    182 		    umem_error_buffer);
    183 	}
    184 
    185 	return (DCMD_OK);
    186 }
    187 
    188 static void
    189 umem_log_status(const char *name, umem_log_header_t *val)
    190 {
    191 	umem_log_header_t my_lh;
    192 	uintptr_t pos = (uintptr_t)val;
    193 	size_t size;
    194 
    195 	if (pos == NULL)
    196 		return;
    197 
    198 	if (mdb_vread(&my_lh, sizeof (umem_log_header_t), pos) == -1) {
    199 		mdb_warn("\nunable to read umem_%s_log pointer %p",
    200 		    name, pos);
    201 		return;
    202 	}
    203 
    204 	size = my_lh.lh_chunksize * my_lh.lh_nchunks;
    205 
    206 	if (size % (1024 * 1024) == 0)
    207 		mdb_printf("%s=%dm ", name, size / (1024 * 1024));
    208 	else if (size % 1024 == 0)
    209 		mdb_printf("%s=%dk ", name, size / 1024);
    210 	else
    211 		mdb_printf("%s=%d ", name, size);
    212 }
    213 
    214 typedef struct umem_debug_flags {
    215 	const char	*udf_name;
    216 	uint_t		udf_flags;
    217 	uint_t		udf_clear;	/* if 0, uses udf_flags */
    218 } umem_debug_flags_t;
    219 
    220 umem_debug_flags_t umem_status_flags[] = {
    221 	{ "random",	UMF_RANDOMIZE,	UMF_RANDOM },
    222 	{ "default",	UMF_AUDIT | UMF_DEADBEEF | UMF_REDZONE | UMF_CONTENTS },
    223 	{ "audit",	UMF_AUDIT },
    224 	{ "guards",	UMF_DEADBEEF | UMF_REDZONE },
    225 	{ "nosignal",	UMF_CHECKSIGNAL },
    226 	{ "firewall",	UMF_FIREWALL },
    227 	{ "lite",	UMF_LITE },
    228 	{ NULL }
    229 };
    230 
    231 /*ARGSUSED*/
    232 int
    233 umem_status(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
    234 {
    235 	int umem_logging;
    236 
    237 	umem_log_header_t *umem_transaction_log;
    238 	umem_log_header_t *umem_content_log;
    239 	umem_log_header_t *umem_failure_log;
    240 	umem_log_header_t *umem_slab_log;
    241 
    242 	mdb_printf("Status:\t\t%s\n",
    243 	    umem_ready == UMEM_READY_INIT_FAILED ? "initialization failed" :
    244 	    umem_ready == UMEM_READY_STARTUP ? "uninitialized" :
    245 	    umem_ready == UMEM_READY_INITING ? "initialization in process" :
    246 	    umem_ready == UMEM_READY ? "ready and active" :
    247 	    umem_ready == 0 ? "not loaded into address space" :
    248 	    "unknown (umem_ready invalid)");
    249 
    250 	if (umem_ready == 0)
    251 		return (DCMD_OK);
    252 
    253 	mdb_printf("Concurrency:\t%d\n", umem_max_ncpus);
    254 
    255 	if (UMEM_READVAR(umem_logging))
    256 		goto err;
    257 	if (UMEM_READVAR(umem_transaction_log))
    258 		goto err;
    259 	if (UMEM_READVAR(umem_content_log))
    260 		goto err;
    261 	if (UMEM_READVAR(umem_failure_log))
    262 		goto err;
    263 	if (UMEM_READVAR(umem_slab_log))
    264 		goto err;
    265 
    266 	mdb_printf("Logs:\t\t");
    267 	umem_log_status("transaction", umem_transaction_log);
    268 	umem_log_status("content", umem_content_log);
    269 	umem_log_status("fail", umem_failure_log);
    270 	umem_log_status("slab", umem_slab_log);
    271 	if (!umem_logging)
    272 		mdb_printf("(inactive)");
    273 	mdb_printf("\n");
    274 
    275 	mdb_printf("Message buffer:\n");
    276 	return (umem_abort_messages());
    277 
    278 err:
    279 	mdb_printf("Message buffer:\n");
    280 	(void) umem_abort_messages();
    281 	return (DCMD_ERR);
    282 }
    283 
    284 typedef struct {
    285 	uintptr_t ucw_first;
    286 	uintptr_t ucw_current;
    287 } umem_cache_walk_t;
    288 
    289 int
    290 umem_cache_walk_init(mdb_walk_state_t *wsp)
    291 {
    292 	umem_cache_walk_t *ucw;
    293 	umem_cache_t c;
    294 	uintptr_t cp;
    295 	GElf_Sym sym;
    296 
    297 	if (umem_lookup_by_name("umem_null_cache", &sym) == -1) {
    298 		mdb_warn("couldn't find umem_null_cache");
    299 		return (WALK_ERR);
    300 	}
    301 
    302 	cp = (uintptr_t)sym.st_value;
    303 
    304 	if (mdb_vread(&c, sizeof (umem_cache_t), cp) == -1) {
    305 		mdb_warn("couldn't read cache at %p", cp);
    306 		return (WALK_ERR);
    307 	}
    308 
    309 	ucw = mdb_alloc(sizeof (umem_cache_walk_t), UM_SLEEP);
    310 
    311 	ucw->ucw_first = cp;
    312 	ucw->ucw_current = (uintptr_t)c.cache_next;
    313 	wsp->walk_data = ucw;
    314 
    315 	return (WALK_NEXT);
    316 }
    317 
    318 int
    319 umem_cache_walk_step(mdb_walk_state_t *wsp)
    320 {
    321 	umem_cache_walk_t *ucw = wsp->walk_data;
    322 	umem_cache_t c;
    323 	int status;
    324 
    325 	if (mdb_vread(&c, sizeof (umem_cache_t), ucw->ucw_current) == -1) {
    326 		mdb_warn("couldn't read cache at %p", ucw->ucw_current);
    327 		return (WALK_DONE);
    328 	}
    329 
    330 	status = wsp->walk_callback(ucw->ucw_current, &c, wsp->walk_cbdata);
    331 
    332 	if ((ucw->ucw_current = (uintptr_t)c.cache_next) == ucw->ucw_first)
    333 		return (WALK_DONE);
    334 
    335 	return (status);
    336 }
    337 
    338 void
    339 umem_cache_walk_fini(mdb_walk_state_t *wsp)
    340 {
    341 	umem_cache_walk_t *ucw = wsp->walk_data;
    342 	mdb_free(ucw, sizeof (umem_cache_walk_t));
    343 }
    344 
    345 typedef struct {
    346 	umem_cpu_t *ucw_cpus;
    347 	uint32_t ucw_current;
    348 	uint32_t ucw_max;
    349 } umem_cpu_walk_state_t;
    350 
    351 int
    352 umem_cpu_walk_init(mdb_walk_state_t *wsp)
    353 {
    354 	umem_cpu_t *umem_cpus;
    355 
    356 	umem_cpu_walk_state_t *ucw;
    357 
    358 	if (umem_readvar(&umem_cpus, "umem_cpus") == -1) {
    359 		mdb_warn("failed to read 'umem_cpus'");
    360 		return (WALK_ERR);
    361 	}
    362 
    363 	ucw = mdb_alloc(sizeof (*ucw), UM_SLEEP);
    364 
    365 	ucw->ucw_cpus = umem_cpus;
    366 	ucw->ucw_current = 0;
    367 	ucw->ucw_max = umem_max_ncpus;
    368 
    369 	wsp->walk_data = ucw;
    370 	return (WALK_NEXT);
    371 }
    372 
    373 int
    374 umem_cpu_walk_step(mdb_walk_state_t *wsp)
    375 {
    376 	umem_cpu_t cpu;
    377 	umem_cpu_walk_state_t *ucw = wsp->walk_data;
    378 
    379 	uintptr_t caddr;
    380 
    381 	if (ucw->ucw_current >= ucw->ucw_max)
    382 		return (WALK_DONE);
    383 
    384 	caddr = (uintptr_t)&(ucw->ucw_cpus[ucw->ucw_current]);
    385 
    386 	if (mdb_vread(&cpu, sizeof (umem_cpu_t), caddr) == -1) {
    387 		mdb_warn("failed to read cpu %d", ucw->ucw_current);
    388 		return (WALK_ERR);
    389 	}
    390 
    391 	ucw->ucw_current++;
    392 
    393 	return (wsp->walk_callback(caddr, &cpu, wsp->walk_cbdata));
    394 }
    395 
    396 void
    397 umem_cpu_walk_fini(mdb_walk_state_t *wsp)
    398 {
    399 	umem_cpu_walk_state_t *ucw = wsp->walk_data;
    400 
    401 	mdb_free(ucw, sizeof (*ucw));
    402 }
    403 
    404 int
    405 umem_cpu_cache_walk_init(mdb_walk_state_t *wsp)
    406 {
    407 	if (wsp->walk_addr == NULL) {
    408 		mdb_warn("umem_cpu_cache doesn't support global walks");
    409 		return (WALK_ERR);
    410 	}
    411 
    412 	if (mdb_layered_walk("umem_cpu", wsp) == -1) {
    413 		mdb_warn("couldn't walk 'umem_cpu'");
    414 		return (WALK_ERR);
    415 	}
    416 
    417 	wsp->walk_data = (void *)wsp->walk_addr;
    418 
    419 	return (WALK_NEXT);
    420 }
    421 
    422 int
    423 umem_cpu_cache_walk_step(mdb_walk_state_t *wsp)
    424 {
    425 	uintptr_t caddr = (uintptr_t)wsp->walk_data;
    426 	const umem_cpu_t *cpu = wsp->walk_layer;
    427 	umem_cpu_cache_t cc;
    428 
    429 	caddr += cpu->cpu_cache_offset;
    430 
    431 	if (mdb_vread(&cc, sizeof (umem_cpu_cache_t), caddr) == -1) {
    432 		mdb_warn("couldn't read umem_cpu_cache at %p", caddr);
    433 		return (WALK_ERR);
    434 	}
    435 
    436 	return (wsp->walk_callback(caddr, &cc, wsp->walk_cbdata));
    437 }
    438 
    439 int
    440 umem_slab_walk_init(mdb_walk_state_t *wsp)
    441 {
    442 	uintptr_t caddr = wsp->walk_addr;
    443 	umem_cache_t c;
    444 
    445 	if (caddr == NULL) {
    446 		mdb_warn("umem_slab doesn't support global walks\n");
    447 		return (WALK_ERR);
    448 	}
    449 
    450 	if (mdb_vread(&c, sizeof (c), caddr) == -1) {
    451 		mdb_warn("couldn't read umem_cache at %p", caddr);
    452 		return (WALK_ERR);
    453 	}
    454 
    455 	wsp->walk_data =
    456 	    (void *)(caddr + offsetof(umem_cache_t, cache_nullslab));
    457 	wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_next;
    458 
    459 	return (WALK_NEXT);
    460 }
    461 
    462 int
    463 umem_slab_walk_partial_init(mdb_walk_state_t *wsp)
    464 {
    465 	uintptr_t caddr = wsp->walk_addr;
    466 	umem_cache_t c;
    467 
    468 	if (caddr == NULL) {
    469 		mdb_warn("umem_slab_partial doesn't support global walks\n");
    470 		return (WALK_ERR);
    471 	}
    472 
    473 	if (mdb_vread(&c, sizeof (c), caddr) == -1) {
    474 		mdb_warn("couldn't read umem_cache at %p", caddr);
    475 		return (WALK_ERR);
    476 	}
    477 
    478 	wsp->walk_data =
    479 	    (void *)(caddr + offsetof(umem_cache_t, cache_nullslab));
    480 	wsp->walk_addr = (uintptr_t)c.cache_freelist;
    481 
    482 	/*
    483 	 * Some consumers (umem_walk_step(), in particular) require at
    484 	 * least one callback if there are any buffers in the cache.  So
    485 	 * if there are *no* partial slabs, report the last full slab, if
    486 	 * any.
    487 	 *
    488 	 * Yes, this is ugly, but it's cleaner than the other possibilities.
    489 	 */
    490 	if ((uintptr_t)wsp->walk_data == wsp->walk_addr)
    491 		wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_prev;
    492 
    493 	return (WALK_NEXT);
    494 }
    495 
    496 int
    497 umem_slab_walk_step(mdb_walk_state_t *wsp)
    498 {
    499 	umem_slab_t s;
    500 	uintptr_t addr = wsp->walk_addr;
    501 	uintptr_t saddr = (uintptr_t)wsp->walk_data;
    502 	uintptr_t caddr = saddr - offsetof(umem_cache_t, cache_nullslab);
    503 
    504 	if (addr == saddr)
    505 		return (WALK_DONE);
    506 
    507 	if (mdb_vread(&s, sizeof (s), addr) == -1) {
    508 		mdb_warn("failed to read slab at %p", wsp->walk_addr);
    509 		return (WALK_ERR);
    510 	}
    511 
    512 	if ((uintptr_t)s.slab_cache != caddr) {
    513 		mdb_warn("slab %p isn't in cache %p (in cache %p)\n",
    514 		    addr, caddr, s.slab_cache);
    515 		return (WALK_ERR);
    516 	}
    517 
    518 	wsp->walk_addr = (uintptr_t)s.slab_next;
    519 
    520 	return (wsp->walk_callback(addr, &s, wsp->walk_cbdata));
    521 }
    522 
    523 int
    524 umem_cache(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
    525 {
    526 	umem_cache_t c;
    527 
    528 	if (!(flags & DCMD_ADDRSPEC)) {
    529 		if (mdb_walk_dcmd("umem_cache", "umem_cache", ac, argv) == -1) {
    530 			mdb_warn("can't walk umem_cache");
    531 			return (DCMD_ERR);
    532 		}
    533 		return (DCMD_OK);
    534 	}
    535 
    536 	if (DCMD_HDRSPEC(flags))
    537 		mdb_printf("%-?s %-25s %4s %8s %8s %8s\n", "ADDR", "NAME",
    538 		    "FLAG", "CFLAG", "BUFSIZE", "BUFTOTL");
    539 
    540 	if (mdb_vread(&c, sizeof (c), addr) == -1) {
    541 		mdb_warn("couldn't read umem_cache at %p", addr);
    542 		return (DCMD_ERR);
    543 	}
    544 
    545 	mdb_printf("%0?p %-25s %04x %08x %8ld %8lld\n", addr, c.cache_name,
    546 	    c.cache_flags, c.cache_cflags, c.cache_bufsize, c.cache_buftotal);
    547 
    548 	return (DCMD_OK);
    549 }
    550 
    551 static int
    552 addrcmp(const void *lhs, const void *rhs)
    553 {
    554 	uintptr_t p1 = *((uintptr_t *)lhs);
    555 	uintptr_t p2 = *((uintptr_t *)rhs);
    556 
    557 	if (p1 < p2)
    558 		return (-1);
    559 	if (p1 > p2)
    560 		return (1);
    561 	return (0);
    562 }
    563 
    564 static int
    565 bufctlcmp(const umem_bufctl_audit_t **lhs, const umem_bufctl_audit_t **rhs)
    566 {
    567 	const umem_bufctl_audit_t *bcp1 = *lhs;
    568 	const umem_bufctl_audit_t *bcp2 = *rhs;
    569 
    570 	if (bcp1->bc_timestamp > bcp2->bc_timestamp)
    571 		return (-1);
    572 
    573 	if (bcp1->bc_timestamp < bcp2->bc_timestamp)
    574 		return (1);
    575 
    576 	return (0);
    577 }
    578 
    579 typedef struct umem_hash_walk {
    580 	uintptr_t *umhw_table;
    581 	size_t umhw_nelems;
    582 	size_t umhw_pos;
    583 	umem_bufctl_t umhw_cur;
    584 } umem_hash_walk_t;
    585 
    586 int
    587 umem_hash_walk_init(mdb_walk_state_t *wsp)
    588 {
    589 	umem_hash_walk_t *umhw;
    590 	uintptr_t *hash;
    591 	umem_cache_t c;
    592 	uintptr_t haddr, addr = wsp->walk_addr;
    593 	size_t nelems;
    594 	size_t hsize;
    595 
    596 	if (addr == NULL) {
    597 		mdb_warn("umem_hash doesn't support global walks\n");
    598 		return (WALK_ERR);
    599 	}
    600 
    601 	if (mdb_vread(&c, sizeof (c), addr) == -1) {
    602 		mdb_warn("couldn't read cache at addr %p", addr);
    603 		return (WALK_ERR);
    604 	}
    605 
    606 	if (!(c.cache_flags & UMF_HASH)) {
    607 		mdb_warn("cache %p doesn't have a hash table\n", addr);
    608 		return (WALK_DONE);		/* nothing to do */
    609 	}
    610 
    611 	umhw = mdb_zalloc(sizeof (umem_hash_walk_t), UM_SLEEP);
    612 	umhw->umhw_cur.bc_next = NULL;
    613 	umhw->umhw_pos = 0;
    614 
    615 	umhw->umhw_nelems = nelems = c.cache_hash_mask + 1;
    616 	hsize = nelems * sizeof (uintptr_t);
    617 	haddr = (uintptr_t)c.cache_hash_table;
    618 
    619 	umhw->umhw_table = hash = mdb_alloc(hsize, UM_SLEEP);
    620 	if (mdb_vread(hash, hsize, haddr) == -1) {
    621 		mdb_warn("failed to read hash table at %p", haddr);
    622 		mdb_free(hash, hsize);
    623 		mdb_free(umhw, sizeof (umem_hash_walk_t));
    624 		return (WALK_ERR);
    625 	}
    626 
    627 	wsp->walk_data = umhw;
    628 
    629 	return (WALK_NEXT);
    630 }
    631 
    632 int
    633 umem_hash_walk_step(mdb_walk_state_t *wsp)
    634 {
    635 	umem_hash_walk_t *umhw = wsp->walk_data;
    636 	uintptr_t addr = NULL;
    637 
    638 	if ((addr = (uintptr_t)umhw->umhw_cur.bc_next) == NULL) {
    639 		while (umhw->umhw_pos < umhw->umhw_nelems) {
    640 			if ((addr = umhw->umhw_table[umhw->umhw_pos++]) != NULL)
    641 				break;
    642 		}
    643 	}
    644 	if (addr == NULL)
    645 		return (WALK_DONE);
    646 
    647 	if (mdb_vread(&umhw->umhw_cur, sizeof (umem_bufctl_t), addr) == -1) {
    648 		mdb_warn("couldn't read umem_bufctl_t at addr %p", addr);
    649 		return (WALK_ERR);
    650 	}
    651 
    652 	return (wsp->walk_callback(addr, &umhw->umhw_cur, wsp->walk_cbdata));
    653 }
    654 
    655 void
    656 umem_hash_walk_fini(mdb_walk_state_t *wsp)
    657 {
    658 	umem_hash_walk_t *umhw = wsp->walk_data;
    659 
    660 	if (umhw == NULL)
    661 		return;
    662 
    663 	mdb_free(umhw->umhw_table, umhw->umhw_nelems * sizeof (uintptr_t));
    664 	mdb_free(umhw, sizeof (umem_hash_walk_t));
    665 }
    666 
    667 /*
    668  * Find the address of the bufctl structure for the address 'buf' in cache
    669  * 'cp', which is at address caddr, and place it in *out.
    670  */
    671 static int
    672 umem_hash_lookup(umem_cache_t *cp, uintptr_t caddr, void *buf, uintptr_t *out)
    673 {
    674 	uintptr_t bucket = (uintptr_t)UMEM_HASH(cp, buf);
    675 	umem_bufctl_t *bcp;
    676 	umem_bufctl_t bc;
    677 
    678 	if (mdb_vread(&bcp, sizeof (umem_bufctl_t *), bucket) == -1) {
    679 		mdb_warn("unable to read hash bucket for %p in cache %p",
    680 		    buf, caddr);
    681 		return (-1);
    682 	}
    683 
    684 	while (bcp != NULL) {
    685 		if (mdb_vread(&bc, sizeof (umem_bufctl_t),
    686 		    (uintptr_t)bcp) == -1) {
    687 			mdb_warn("unable to read bufctl at %p", bcp);
    688 			return (-1);
    689 		}
    690 		if (bc.bc_addr == buf) {
    691 			*out = (uintptr_t)bcp;
    692 			return (0);
    693 		}
    694 		bcp = bc.bc_next;
    695 	}
    696 
    697 	mdb_warn("unable to find bufctl for %p in cache %p\n", buf, caddr);
    698 	return (-1);
    699 }
    700 
    701 int
    702 umem_get_magsize(const umem_cache_t *cp)
    703 {
    704 	uintptr_t addr = (uintptr_t)cp->cache_magtype;
    705 	GElf_Sym mt_sym;
    706 	umem_magtype_t mt;
    707 	int res;
    708 
    709 	/*
    710 	 * if cpu 0 has a non-zero magsize, it must be correct.  caches
    711 	 * with UMF_NOMAGAZINE have disabled their magazine layers, so
    712 	 * it is okay to return 0 for them.
    713 	 */
    714 	if ((res = cp->cache_cpu[0].cc_magsize) != 0 ||
    715 	    (cp->cache_flags & UMF_NOMAGAZINE))
    716 		return (res);
    717 
    718 	if (umem_lookup_by_name("umem_magtype", &mt_sym) == -1) {
    719 		mdb_warn("unable to read 'umem_magtype'");
    720 	} else if (addr < mt_sym.st_value ||
    721 	    addr + sizeof (mt) - 1 > mt_sym.st_value + mt_sym.st_size - 1 ||
    722 	    ((addr - mt_sym.st_value) % sizeof (mt)) != 0) {
    723 		mdb_warn("cache '%s' has invalid magtype pointer (%p)\n",
    724 		    cp->cache_name, addr);
    725 		return (0);
    726 	}
    727 	if (mdb_vread(&mt, sizeof (mt), addr) == -1) {
    728 		mdb_warn("unable to read magtype at %a", addr);
    729 		return (0);
    730 	}
    731 	return (mt.mt_magsize);
    732 }
    733 
    734 /*ARGSUSED*/
    735 static int
    736 umem_estimate_slab(uintptr_t addr, const umem_slab_t *sp, size_t *est)
    737 {
    738 	*est -= (sp->slab_chunks - sp->slab_refcnt);
    739 
    740 	return (WALK_NEXT);
    741 }
    742 
    743 /*
    744  * Returns an upper bound on the number of allocated buffers in a given
    745  * cache.
    746  */
    747 size_t
    748 umem_estimate_allocated(uintptr_t addr, const umem_cache_t *cp)
    749 {
    750 	int magsize;
    751 	size_t cache_est;
    752 
    753 	cache_est = cp->cache_buftotal;
    754 
    755 	(void) mdb_pwalk("umem_slab_partial",
    756 	    (mdb_walk_cb_t)umem_estimate_slab, &cache_est, addr);
    757 
    758 	if ((magsize = umem_get_magsize(cp)) != 0) {
    759 		size_t mag_est = cp->cache_full.ml_total * magsize;
    760 
    761 		if (cache_est >= mag_est) {
    762 			cache_est -= mag_est;
    763 		} else {
    764 			mdb_warn("cache %p's magazine layer holds more buffers "
    765 			    "than the slab layer.\n", addr);
    766 		}
    767 	}
    768 	return (cache_est);
    769 }
    770 
    771 #define	READMAG_ROUNDS(rounds) { \
    772 	if (mdb_vread(mp, magbsize, (uintptr_t)ump) == -1) { \
    773 		mdb_warn("couldn't read magazine at %p", ump); \
    774 		goto fail; \
    775 	} \
    776 	for (i = 0; i < rounds; i++) { \
    777 		maglist[magcnt++] = mp->mag_round[i]; \
    778 		if (magcnt == magmax) { \
    779 			mdb_warn("%d magazines exceeds fudge factor\n", \
    780 			    magcnt); \
    781 			goto fail; \
    782 		} \
    783 	} \
    784 }
    785 
    786 int
    787 umem_read_magazines(umem_cache_t *cp, uintptr_t addr,
    788     void ***maglistp, size_t *magcntp, size_t *magmaxp, int alloc_flags)
    789 {
    790 	umem_magazine_t *ump, *mp;
    791 	void **maglist = NULL;
    792 	int i, cpu;
    793 	size_t magsize, magmax, magbsize;
    794 	size_t magcnt = 0;
    795 
    796 	/*
    797 	 * Read the magtype out of the cache, after verifying the pointer's
    798 	 * correctness.
    799 	 */
    800 	magsize = umem_get_magsize(cp);
    801 	if (magsize == 0) {
    802 		*maglistp = NULL;
    803 		*magcntp = 0;
    804 		*magmaxp = 0;
    805 		return (WALK_NEXT);
    806 	}
    807 
    808 	/*
    809 	 * There are several places where we need to go buffer hunting:
    810 	 * the per-CPU loaded magazine, the per-CPU spare full magazine,
    811 	 * and the full magazine list in the depot.
    812 	 *
    813 	 * For an upper bound on the number of buffers in the magazine
    814 	 * layer, we have the number of magazines on the cache_full
    815 	 * list plus at most two magazines per CPU (the loaded and the
    816 	 * spare).  Toss in 100 magazines as a fudge factor in case this
    817 	 * is live (the number "100" comes from the same fudge factor in
    818 	 * crash(1M)).
    819 	 */
    820 	magmax = (cp->cache_full.ml_total + 2 * umem_max_ncpus + 100) * magsize;
    821 	magbsize = offsetof(umem_magazine_t, mag_round[magsize]);
    822 
    823 	if (magbsize >= PAGESIZE / 2) {
    824 		mdb_warn("magazine size for cache %p unreasonable (%x)\n",
    825 		    addr, magbsize);
    826 		return (WALK_ERR);
    827 	}
    828 
    829 	maglist = mdb_alloc(magmax * sizeof (void *), alloc_flags);
    830 	mp = mdb_alloc(magbsize, alloc_flags);
    831 	if (mp == NULL || maglist == NULL)
    832 		goto fail;
    833 
    834 	/*
    835 	 * First up: the magazines in the depot (i.e. on the cache_full list).
    836 	 */
    837 	for (ump = cp->cache_full.ml_list; ump != NULL; ) {
    838 		READMAG_ROUNDS(magsize);
    839 		ump = mp->mag_next;
    840 
    841 		if (ump == cp->cache_full.ml_list)
    842 			break; /* cache_full list loop detected */
    843 	}
    844 
    845 	dprintf(("cache_full list done\n"));
    846 
    847 	/*
    848 	 * Now whip through the CPUs, snagging the loaded magazines
    849 	 * and full spares.
    850 	 */
    851 	for (cpu = 0; cpu < umem_max_ncpus; cpu++) {
    852 		umem_cpu_cache_t *ccp = &cp->cache_cpu[cpu];
    853 
    854 		dprintf(("reading cpu cache %p\n",
    855 		    (uintptr_t)ccp - (uintptr_t)cp + addr));
    856 
    857 		if (ccp->cc_rounds > 0 &&
    858 		    (ump = ccp->cc_loaded) != NULL) {
    859 			dprintf(("reading %d loaded rounds\n", ccp->cc_rounds));
    860 			READMAG_ROUNDS(ccp->cc_rounds);
    861 		}
    862 
    863 		if (ccp->cc_prounds > 0 &&
    864 		    (ump = ccp->cc_ploaded) != NULL) {
    865 			dprintf(("reading %d previously loaded rounds\n",
    866 			    ccp->cc_prounds));
    867 			READMAG_ROUNDS(ccp->cc_prounds);
    868 		}
    869 	}
    870 
    871 	dprintf(("magazine layer: %d buffers\n", magcnt));
    872 
    873 	if (!(alloc_flags & UM_GC))
    874 		mdb_free(mp, magbsize);
    875 
    876 	*maglistp = maglist;
    877 	*magcntp = magcnt;
    878 	*magmaxp = magmax;
    879 
    880 	return (WALK_NEXT);
    881 
    882 fail:
    883 	if (!(alloc_flags & UM_GC)) {
    884 		if (mp)
    885 			mdb_free(mp, magbsize);
    886 		if (maglist)
    887 			mdb_free(maglist, magmax * sizeof (void *));
    888 	}
    889 	return (WALK_ERR);
    890 }
    891 
    892 static int
    893 umem_walk_callback(mdb_walk_state_t *wsp, uintptr_t buf)
    894 {
    895 	return (wsp->walk_callback(buf, NULL, wsp->walk_cbdata));
    896 }
    897 
    898 static int
    899 bufctl_walk_callback(umem_cache_t *cp, mdb_walk_state_t *wsp, uintptr_t buf)
    900 {
    901 	umem_bufctl_audit_t *b;
    902 	UMEM_LOCAL_BUFCTL_AUDIT(&b);
    903 
    904 	/*
    905 	 * if UMF_AUDIT is not set, we know that we're looking at a
    906 	 * umem_bufctl_t.
    907 	 */
    908 	if (!(cp->cache_flags & UMF_AUDIT) ||
    909 	    mdb_vread(b, UMEM_BUFCTL_AUDIT_SIZE, buf) == -1) {
    910 		(void) memset(b, 0, UMEM_BUFCTL_AUDIT_SIZE);
    911 		if (mdb_vread(b, sizeof (umem_bufctl_t), buf) == -1) {
    912 			mdb_warn("unable to read bufctl at %p", buf);
    913 			return (WALK_ERR);
    914 		}
    915 	}
    916 
    917 	return (wsp->walk_callback(buf, b, wsp->walk_cbdata));
    918 }
    919 
    920 typedef struct umem_walk {
    921 	int umw_type;
    922 
    923 	int umw_addr;			/* cache address */
    924 	umem_cache_t *umw_cp;
    925 	size_t umw_csize;
    926 
    927 	/*
    928 	 * magazine layer
    929 	 */
    930 	void **umw_maglist;
    931 	size_t umw_max;
    932 	size_t umw_count;
    933 	size_t umw_pos;
    934 
    935 	/*
    936 	 * slab layer
    937 	 */
    938 	char *umw_valid;	/* to keep track of freed buffers */
    939 	char *umw_ubase;	/* buffer for slab data */
    940 } umem_walk_t;
    941 
    942 static int
    943 umem_walk_init_common(mdb_walk_state_t *wsp, int type)
    944 {
    945 	umem_walk_t *umw;
    946 	int csize;
    947 	umem_cache_t *cp;
    948 	size_t vm_quantum;
    949 
    950 	size_t magmax, magcnt;
    951 	void **maglist = NULL;
    952 	uint_t chunksize, slabsize;
    953 	int status = WALK_ERR;
    954 	uintptr_t addr = wsp->walk_addr;
    955 	const char *layered;
    956 
    957 	type &= ~UM_HASH;
    958 
    959 	if (addr == NULL) {
    960 		mdb_warn("umem walk doesn't support global walks\n");
    961 		return (WALK_ERR);
    962 	}
    963 
    964 	dprintf(("walking %p\n", addr));
    965 
    966 	/*
    967 	 * The number of "cpus" determines how large the cache is.
    968 	 */
    969 	csize = UMEM_CACHE_SIZE(umem_max_ncpus);
    970 	cp = mdb_alloc(csize, UM_SLEEP);
    971 
    972 	if (mdb_vread(cp, csize, addr) == -1) {
    973 		mdb_warn("couldn't read cache at addr %p", addr);
    974 		goto out2;
    975 	}
    976 
    977 	/*
    978 	 * It's easy for someone to hand us an invalid cache address.
    979 	 * Unfortunately, it is hard for this walker to survive an
    980 	 * invalid cache cleanly.  So we make sure that:
    981 	 *
    982 	 *	1. the vmem arena for the cache is readable,
    983 	 *	2. the vmem arena's quantum is a power of 2,
    984 	 *	3. our slabsize is a multiple of the quantum, and
    985 	 *	4. our chunksize is >0 and less than our slabsize.
    986 	 */
    987 	if (mdb_vread(&vm_quantum, sizeof (vm_quantum),
    988 	    (uintptr_t)&cp->cache_arena->vm_quantum) == -1 ||
    989 	    vm_quantum == 0 ||
    990 	    (vm_quantum & (vm_quantum - 1)) != 0 ||
    991 	    cp->cache_slabsize < vm_quantum ||
    992 	    P2PHASE(cp->cache_slabsize, vm_quantum) != 0 ||
    993 	    cp->cache_chunksize == 0 ||
    994 	    cp->cache_chunksize > cp->cache_slabsize) {
    995 		mdb_warn("%p is not a valid umem_cache_t\n", addr);
    996 		goto out2;
    997 	}
    998 
    999 	dprintf(("buf total is %d\n", cp->cache_buftotal));
   1000 
   1001 	if (cp->cache_buftotal == 0) {
   1002 		mdb_free(cp, csize);
   1003 		return (WALK_DONE);
   1004 	}
   1005 
   1006 	/*
   1007 	 * If they ask for bufctls, but it's a small-slab cache,
   1008 	 * there is nothing to report.
   1009 	 */
   1010 	if ((type & UM_BUFCTL) && !(cp->cache_flags & UMF_HASH)) {
   1011 		dprintf(("bufctl requested, not UMF_HASH (flags: %p)\n",
   1012 		    cp->cache_flags));
   1013 		mdb_free(cp, csize);
   1014 		return (WALK_DONE);
   1015 	}
   1016 
   1017 	/*
   1018 	 * Read in the contents of the magazine layer
   1019 	 */
   1020 	if (umem_read_magazines(cp, addr, &maglist, &magcnt, &magmax,
   1021 	    UM_SLEEP) == WALK_ERR)
   1022 		goto out2;
   1023 
   1024 	/*
   1025 	 * We have all of the buffers from the magazines;  if we are walking
   1026 	 * allocated buffers, sort them so we can bsearch them later.
   1027 	 */
   1028 	if (type & UM_ALLOCATED)
   1029 		qsort(maglist, magcnt, sizeof (void *), addrcmp);
   1030 
   1031 	wsp->walk_data = umw = mdb_zalloc(sizeof (umem_walk_t), UM_SLEEP);
   1032 
   1033 	umw->umw_type = type;
   1034 	umw->umw_addr = addr;
   1035 	umw->umw_cp = cp;
   1036 	umw->umw_csize = csize;
   1037 	umw->umw_maglist = maglist;
   1038 	umw->umw_max = magmax;
   1039 	umw->umw_count = magcnt;
   1040 	umw->umw_pos = 0;
   1041 
   1042 	/*
   1043 	 * When walking allocated buffers in a UMF_HASH cache, we walk the
   1044 	 * hash table instead of the slab layer.
   1045 	 */
   1046 	if ((cp->cache_flags & UMF_HASH) && (type & UM_ALLOCATED)) {
   1047 		layered = "umem_hash";
   1048 
   1049 		umw->umw_type |= UM_HASH;
   1050 	} else {
   1051 		/*
   1052 		 * If we are walking freed buffers, we only need the
   1053 		 * magazine layer plus the partially allocated slabs.
   1054 		 * To walk allocated buffers, we need all of the slabs.
   1055 		 */
   1056 		if (type & UM_ALLOCATED)
   1057 			layered = "umem_slab";
   1058 		else
   1059 			layered = "umem_slab_partial";
   1060 
   1061 		/*
   1062 		 * for small-slab caches, we read in the entire slab.  For
   1063 		 * freed buffers, we can just walk the freelist.  For
   1064 		 * allocated buffers, we use a 'valid' array to track
   1065 		 * the freed buffers.
   1066 		 */
   1067 		if (!(cp->cache_flags & UMF_HASH)) {
   1068 			chunksize = cp->cache_chunksize;
   1069 			slabsize = cp->cache_slabsize;
   1070 
   1071 			umw->umw_ubase = mdb_alloc(slabsize +
   1072 			    sizeof (umem_bufctl_t), UM_SLEEP);
   1073 
   1074 			if (type & UM_ALLOCATED)
   1075 				umw->umw_valid =
   1076 				    mdb_alloc(slabsize / chunksize, UM_SLEEP);
   1077 		}
   1078 	}
   1079 
   1080 	status = WALK_NEXT;
   1081 
   1082 	if (mdb_layered_walk(layered, wsp) == -1) {
   1083 		mdb_warn("unable to start layered '%s' walk", layered);
   1084 		status = WALK_ERR;
   1085 	}
   1086 
   1087 out1:
   1088 	if (status == WALK_ERR) {
   1089 		if (umw->umw_valid)
   1090 			mdb_free(umw->umw_valid, slabsize / chunksize);
   1091 
   1092 		if (umw->umw_ubase)
   1093 			mdb_free(umw->umw_ubase, slabsize +
   1094 			    sizeof (umem_bufctl_t));
   1095 
   1096 		if (umw->umw_maglist)
   1097 			mdb_free(umw->umw_maglist, umw->umw_max *
   1098 			    sizeof (uintptr_t));
   1099 
   1100 		mdb_free(umw, sizeof (umem_walk_t));
   1101 		wsp->walk_data = NULL;
   1102 	}
   1103 
   1104 out2:
   1105 	if (status == WALK_ERR)
   1106 		mdb_free(cp, csize);
   1107 
   1108 	return (status);
   1109 }
   1110 
   1111 int
   1112 umem_walk_step(mdb_walk_state_t *wsp)
   1113 {
   1114 	umem_walk_t *umw = wsp->walk_data;
   1115 	int type = umw->umw_type;
   1116 	umem_cache_t *cp = umw->umw_cp;
   1117 
   1118 	void **maglist = umw->umw_maglist;
   1119 	int magcnt = umw->umw_count;
   1120 
   1121 	uintptr_t chunksize, slabsize;
   1122 	uintptr_t addr;
   1123 	const umem_slab_t *sp;
   1124 	const umem_bufctl_t *bcp;
   1125 	umem_bufctl_t bc;
   1126 
   1127 	int chunks;
   1128 	char *kbase;
   1129 	void *buf;
   1130 	int i, ret;
   1131 
   1132 	char *valid, *ubase;
   1133 
   1134 	/*
   1135 	 * first, handle the 'umem_hash' layered walk case
   1136 	 */
   1137 	if (type & UM_HASH) {
   1138 		/*
   1139 		 * We have a buffer which has been allocated out of the
   1140 		 * global layer. We need to make sure that it's not
   1141 		 * actually sitting in a magazine before we report it as
   1142 		 * an allocated buffer.
   1143 		 */
   1144 		buf = ((const umem_bufctl_t *)wsp->walk_layer)->bc_addr;
   1145 
   1146 		if (magcnt > 0 &&
   1147 		    bsearch(&buf, maglist, magcnt, sizeof (void *),
   1148 		    addrcmp) != NULL)
   1149 			return (WALK_NEXT);
   1150 
   1151 		if (type & UM_BUFCTL)
   1152 			return (bufctl_walk_callback(cp, wsp, wsp->walk_addr));
   1153 
   1154 		return (umem_walk_callback(wsp, (uintptr_t)buf));
   1155 	}
   1156 
   1157 	ret = WALK_NEXT;
   1158 
   1159 	addr = umw->umw_addr;
   1160 
   1161 	/*
   1162 	 * If we're walking freed buffers, report everything in the
   1163 	 * magazine layer before processing the first slab.
   1164 	 */
   1165 	if ((type & UM_FREE) && magcnt != 0) {
   1166 		umw->umw_count = 0;		/* only do this once */
   1167 		for (i = 0; i < magcnt; i++) {
   1168 			buf = maglist[i];
   1169 
   1170 			if (type & UM_BUFCTL) {
   1171 				uintptr_t out;
   1172 
   1173 				if (cp->cache_flags & UMF_BUFTAG) {
   1174 					umem_buftag_t *btp;
   1175 					umem_buftag_t tag;
   1176 
   1177 					/* LINTED - alignment */
   1178 					btp = UMEM_BUFTAG(cp, buf);
   1179 					if (mdb_vread(&tag, sizeof (tag),
   1180 					    (uintptr_t)btp) == -1) {
   1181 						mdb_warn("reading buftag for "
   1182 						    "%p at %p", buf, btp);
   1183 						continue;
   1184 					}
   1185 					out = (uintptr_t)tag.bt_bufctl;
   1186 				} else {
   1187 					if (umem_hash_lookup(cp, addr, buf,
   1188 					    &out) == -1)
   1189 						continue;
   1190 				}
   1191 				ret = bufctl_walk_callback(cp, wsp, out);
   1192 			} else {
   1193 				ret = umem_walk_callback(wsp, (uintptr_t)buf);
   1194 			}
   1195 
   1196 			if (ret != WALK_NEXT)
   1197 				return (ret);
   1198 		}
   1199 	}
   1200 
   1201 	/*
   1202 	 * Handle the buffers in the current slab
   1203 	 */
   1204 	chunksize = cp->cache_chunksize;
   1205 	slabsize = cp->cache_slabsize;
   1206 
   1207 	sp = wsp->walk_layer;
   1208 	chunks = sp->slab_chunks;
   1209 	kbase = sp->slab_base;
   1210 
   1211 	dprintf(("kbase is %p\n", kbase));
   1212 
   1213 	if (!(cp->cache_flags & UMF_HASH)) {
   1214 		valid = umw->umw_valid;
   1215 		ubase = umw->umw_ubase;
   1216 
   1217 		if (mdb_vread(ubase, chunks * chunksize,
   1218 		    (uintptr_t)kbase) == -1) {
   1219 			mdb_warn("failed to read slab contents at %p", kbase);
   1220 			return (WALK_ERR);
   1221 		}
   1222 
   1223 		/*
   1224 		 * Set up the valid map as fully allocated -- we'll punch
   1225 		 * out the freelist.
   1226 		 */
   1227 		if (type & UM_ALLOCATED)
   1228 			(void) memset(valid, 1, chunks);
   1229 	} else {
   1230 		valid = NULL;
   1231 		ubase = NULL;
   1232 	}
   1233 
   1234 	/*
   1235 	 * walk the slab's freelist
   1236 	 */
   1237 	bcp = sp->slab_head;
   1238 
   1239 	dprintf(("refcnt is %d; chunks is %d\n", sp->slab_refcnt, chunks));
   1240 
   1241 	/*
   1242 	 * since we could be in the middle of allocating a buffer,
   1243 	 * our refcnt could be one higher than it aught.  So we
   1244 	 * check one further on the freelist than the count allows.
   1245 	 */
   1246 	for (i = sp->slab_refcnt; i <= chunks; i++) {
   1247 		uint_t ndx;
   1248 
   1249 		dprintf(("bcp is %p\n", bcp));
   1250 
   1251 		if (bcp == NULL) {
   1252 			if (i == chunks)
   1253 				break;
   1254 			mdb_warn(
   1255 			    "slab %p in cache %p freelist too short by %d\n",
   1256 			    sp, addr, chunks - i);
   1257 			break;
   1258 		}
   1259 
   1260 		if (cp->cache_flags & UMF_HASH) {
   1261 			if (mdb_vread(&bc, sizeof (bc), (uintptr_t)bcp) == -1) {
   1262 				mdb_warn("failed to read bufctl ptr at %p",
   1263 				    bcp);
   1264 				break;
   1265 			}
   1266 			buf = bc.bc_addr;
   1267 		} else {
   1268 			/*
   1269 			 * Otherwise the buffer is in the slab which
   1270 			 * we've read in;  we just need to determine
   1271 			 * its offset in the slab to find the
   1272 			 * umem_bufctl_t.
   1273 			 */
   1274 			bc = *((umem_bufctl_t *)
   1275 			    ((uintptr_t)bcp - (uintptr_t)kbase +
   1276 			    (uintptr_t)ubase));
   1277 
   1278 			buf = UMEM_BUF(cp, bcp);
   1279 		}
   1280 
   1281 		ndx = ((uintptr_t)buf - (uintptr_t)kbase) / chunksize;
   1282 
   1283 		if (ndx > slabsize / cp->cache_bufsize) {
   1284 			/*
   1285 			 * This is very wrong; we have managed to find
   1286 			 * a buffer in the slab which shouldn't
   1287 			 * actually be here.  Emit a warning, and
   1288 			 * try to continue.
   1289 			 */
   1290 			mdb_warn("buf %p is out of range for "
   1291 			    "slab %p, cache %p\n", buf, sp, addr);
   1292 		} else if (type & UM_ALLOCATED) {
   1293 			/*
   1294 			 * we have found a buffer on the slab's freelist;
   1295 			 * clear its entry
   1296 			 */
   1297 			valid[ndx] = 0;
   1298 		} else {
   1299 			/*
   1300 			 * Report this freed buffer
   1301 			 */
   1302 			if (type & UM_BUFCTL) {
   1303 				ret = bufctl_walk_callback(cp, wsp,
   1304 				    (uintptr_t)bcp);
   1305 			} else {
   1306 				ret = umem_walk_callback(wsp, (uintptr_t)buf);
   1307 			}
   1308 			if (ret != WALK_NEXT)
   1309 				return (ret);
   1310 		}
   1311 
   1312 		bcp = bc.bc_next;
   1313 	}
   1314 
   1315 	if (bcp != NULL) {
   1316 		dprintf(("slab %p in cache %p freelist too long (%p)\n",
   1317 		    sp, addr, bcp));
   1318 	}
   1319 
   1320 	/*
   1321 	 * If we are walking freed buffers, the loop above handled reporting
   1322 	 * them.
   1323 	 */
   1324 	if (type & UM_FREE)
   1325 		return (WALK_NEXT);
   1326 
   1327 	if (type & UM_BUFCTL) {
   1328 		mdb_warn("impossible situation: small-slab UM_BUFCTL walk for "
   1329 		    "cache %p\n", addr);
   1330 		return (WALK_ERR);
   1331 	}
   1332 
   1333 	/*
   1334 	 * Report allocated buffers, skipping buffers in the magazine layer.
   1335 	 * We only get this far for small-slab caches.
   1336 	 */
   1337 	for (i = 0; ret == WALK_NEXT && i < chunks; i++) {
   1338 		buf = (char *)kbase + i * chunksize;
   1339 
   1340 		if (!valid[i])
   1341 			continue;		/* on slab freelist */
   1342 
   1343 		if (magcnt > 0 &&
   1344 		    bsearch(&buf, maglist, magcnt, sizeof (void *),
   1345 		    addrcmp) != NULL)
   1346 			continue;		/* in magazine layer */
   1347 
   1348 		ret = umem_walk_callback(wsp, (uintptr_t)buf);
   1349 	}
   1350 	return (ret);
   1351 }
   1352 
   1353 void
   1354 umem_walk_fini(mdb_walk_state_t *wsp)
   1355 {
   1356 	umem_walk_t *umw = wsp->walk_data;
   1357 	uintptr_t chunksize;
   1358 	uintptr_t slabsize;
   1359 
   1360 	if (umw == NULL)
   1361 		return;
   1362 
   1363 	if (umw->umw_maglist != NULL)
   1364 		mdb_free(umw->umw_maglist, umw->umw_max * sizeof (void *));
   1365 
   1366 	chunksize = umw->umw_cp->cache_chunksize;
   1367 	slabsize = umw->umw_cp->cache_slabsize;
   1368 
   1369 	if (umw->umw_valid != NULL)
   1370 		mdb_free(umw->umw_valid, slabsize / chunksize);
   1371 	if (umw->umw_ubase != NULL)
   1372 		mdb_free(umw->umw_ubase, slabsize + sizeof (umem_bufctl_t));
   1373 
   1374 	mdb_free(umw->umw_cp, umw->umw_csize);
   1375 	mdb_free(umw, sizeof (umem_walk_t));
   1376 }
   1377 
   1378 /*ARGSUSED*/
   1379 static int
   1380 umem_walk_all(uintptr_t addr, const umem_cache_t *c, mdb_walk_state_t *wsp)
   1381 {
   1382 	/*
   1383 	 * Buffers allocated from NOTOUCH caches can also show up as freed
   1384 	 * memory in other caches.  This can be a little confusing, so we
   1385 	 * don't walk NOTOUCH caches when walking all caches (thereby assuring
   1386 	 * that "::walk umem" and "::walk freemem" yield disjoint output).
   1387 	 */
   1388 	if (c->cache_cflags & UMC_NOTOUCH)
   1389 		return (WALK_NEXT);
   1390 
   1391 	if (mdb_pwalk(wsp->walk_data, wsp->walk_callback,
   1392 	    wsp->walk_cbdata, addr) == -1)
   1393 		return (WALK_DONE);
   1394 
   1395 	return (WALK_NEXT);
   1396 }
   1397 
   1398 #define	UMEM_WALK_ALL(name, wsp) { \
   1399 	wsp->walk_data = (name); \
   1400 	if (mdb_walk("umem_cache", (mdb_walk_cb_t)umem_walk_all, wsp) == -1) \
   1401 		return (WALK_ERR); \
   1402 	return (WALK_DONE); \
   1403 }
   1404 
   1405 int
   1406 umem_walk_init(mdb_walk_state_t *wsp)
   1407 {
   1408 	if (wsp->walk_arg != NULL)
   1409 		wsp->walk_addr = (uintptr_t)wsp->walk_arg;
   1410 
   1411 	if (wsp->walk_addr == NULL)
   1412 		UMEM_WALK_ALL("umem", wsp);
   1413 	return (umem_walk_init_common(wsp, UM_ALLOCATED));
   1414 }
   1415 
   1416 int
   1417 bufctl_walk_init(mdb_walk_state_t *wsp)
   1418 {
   1419 	if (wsp->walk_addr == NULL)
   1420 		UMEM_WALK_ALL("bufctl", wsp);
   1421 	return (umem_walk_init_common(wsp, UM_ALLOCATED | UM_BUFCTL));
   1422 }
   1423 
   1424 int
   1425 freemem_walk_init(mdb_walk_state_t *wsp)
   1426 {
   1427 	if (wsp->walk_addr == NULL)
   1428 		UMEM_WALK_ALL("freemem", wsp);
   1429 	return (umem_walk_init_common(wsp, UM_FREE));
   1430 }
   1431 
   1432 int
   1433 freectl_walk_init(mdb_walk_state_t *wsp)
   1434 {
   1435 	if (wsp->walk_addr == NULL)
   1436 		UMEM_WALK_ALL("freectl", wsp);
   1437 	return (umem_walk_init_common(wsp, UM_FREE | UM_BUFCTL));
   1438 }
   1439 
   1440 typedef struct bufctl_history_walk {
   1441 	void		*bhw_next;
   1442 	umem_cache_t	*bhw_cache;
   1443 	umem_slab_t	*bhw_slab;
   1444 	hrtime_t	bhw_timestamp;
   1445 } bufctl_history_walk_t;
   1446 
   1447 int
   1448 bufctl_history_walk_init(mdb_walk_state_t *wsp)
   1449 {
   1450 	bufctl_history_walk_t *bhw;
   1451 	umem_bufctl_audit_t bc;
   1452 	umem_bufctl_audit_t bcn;
   1453 
   1454 	if (wsp->walk_addr == NULL) {
   1455 		mdb_warn("bufctl_history walk doesn't support global walks\n");
   1456 		return (WALK_ERR);
   1457 	}
   1458 
   1459 	if (mdb_vread(&bc, sizeof (bc), wsp->walk_addr) == -1) {
   1460 		mdb_warn("unable to read bufctl at %p", wsp->walk_addr);
   1461 		return (WALK_ERR);
   1462 	}
   1463 
   1464 	bhw = mdb_zalloc(sizeof (*bhw), UM_SLEEP);
   1465 	bhw->bhw_timestamp = 0;
   1466 	bhw->bhw_cache = bc.bc_cache;
   1467 	bhw->bhw_slab = bc.bc_slab;
   1468 
   1469 	/*
   1470 	 * sometimes the first log entry matches the base bufctl;  in that
   1471 	 * case, skip the base bufctl.
   1472 	 */
   1473 	if (bc.bc_lastlog != NULL &&
   1474 	    mdb_vread(&bcn, sizeof (bcn), (uintptr_t)bc.bc_lastlog) != -1 &&
   1475 	    bc.bc_addr == bcn.bc_addr &&
   1476 	    bc.bc_cache == bcn.bc_cache &&
   1477 	    bc.bc_slab == bcn.bc_slab &&
   1478 	    bc.bc_timestamp == bcn.bc_timestamp &&
   1479 	    bc.bc_thread == bcn.bc_thread)
   1480 		bhw->bhw_next = bc.bc_lastlog;
   1481 	else
   1482 		bhw->bhw_next = (void *)wsp->walk_addr;
   1483 
   1484 	wsp->walk_addr = (uintptr_t)bc.bc_addr;
   1485 	wsp->walk_data = bhw;
   1486 
   1487 	return (WALK_NEXT);
   1488 }
   1489 
   1490 int
   1491 bufctl_history_walk_step(mdb_walk_state_t *wsp)
   1492 {
   1493 	bufctl_history_walk_t *bhw = wsp->walk_data;
   1494 	uintptr_t addr = (uintptr_t)bhw->bhw_next;
   1495 	uintptr_t baseaddr = wsp->walk_addr;
   1496 	umem_bufctl_audit_t *b;
   1497 	UMEM_LOCAL_BUFCTL_AUDIT(&b);
   1498 
   1499 	if (addr == NULL)
   1500 		return (WALK_DONE);
   1501 
   1502 	if (mdb_vread(b, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) {
   1503 		mdb_warn("unable to read bufctl at %p", bhw->bhw_next);
   1504 		return (WALK_ERR);
   1505 	}
   1506 
   1507 	/*
   1508 	 * The bufctl is only valid if the address, cache, and slab are
   1509 	 * correct.  We also check that the timestamp is decreasing, to
   1510 	 * prevent infinite loops.
   1511 	 */
   1512 	if ((uintptr_t)b->bc_addr != baseaddr ||
   1513 	    b->bc_cache != bhw->bhw_cache ||
   1514 	    b->bc_slab != bhw->bhw_slab ||
   1515 	    (bhw->bhw_timestamp != 0 && b->bc_timestamp >= bhw->bhw_timestamp))
   1516 		return (WALK_DONE);
   1517 
   1518 	bhw->bhw_next = b->bc_lastlog;
   1519 	bhw->bhw_timestamp = b->bc_timestamp;
   1520 
   1521 	return (wsp->walk_callback(addr, b, wsp->walk_cbdata));
   1522 }
   1523 
   1524 void
   1525 bufctl_history_walk_fini(mdb_walk_state_t *wsp)
   1526 {
   1527 	bufctl_history_walk_t *bhw = wsp->walk_data;
   1528 
   1529 	mdb_free(bhw, sizeof (*bhw));
   1530 }
   1531 
   1532 typedef struct umem_log_walk {
   1533 	umem_bufctl_audit_t *ulw_base;
   1534 	umem_bufctl_audit_t **ulw_sorted;
   1535 	umem_log_header_t ulw_lh;
   1536 	size_t ulw_size;
   1537 	size_t ulw_maxndx;
   1538 	size_t ulw_ndx;
   1539 } umem_log_walk_t;
   1540 
   1541 int
   1542 umem_log_walk_init(mdb_walk_state_t *wsp)
   1543 {
   1544 	uintptr_t lp = wsp->walk_addr;
   1545 	umem_log_walk_t *ulw;
   1546 	umem_log_header_t *lhp;
   1547 	int maxndx, i, j, k;
   1548 
   1549 	/*
   1550 	 * By default (global walk), walk the umem_transaction_log.  Otherwise
   1551 	 * read the log whose umem_log_header_t is stored at walk_addr.
   1552 	 */
   1553 	if (lp == NULL && umem_readvar(&lp, "umem_transaction_log") == -1) {
   1554 		mdb_warn("failed to read 'umem_transaction_log'");
   1555 		return (WALK_ERR);
   1556 	}
   1557 
   1558 	if (lp == NULL) {
   1559 		mdb_warn("log is disabled\n");
   1560 		return (WALK_ERR);
   1561 	}
   1562 
   1563 	ulw = mdb_zalloc(sizeof (umem_log_walk_t), UM_SLEEP);
   1564 	lhp = &ulw->ulw_lh;
   1565 
   1566 	if (mdb_vread(lhp, sizeof (umem_log_header_t), lp) == -1) {
   1567 		mdb_warn("failed to read log header at %p", lp);
   1568 		mdb_free(ulw, sizeof (umem_log_walk_t));
   1569 		return (WALK_ERR);
   1570 	}
   1571 
   1572 	ulw->ulw_size = lhp->lh_chunksize * lhp->lh_nchunks;
   1573 	ulw->ulw_base = mdb_alloc(ulw->ulw_size, UM_SLEEP);
   1574 	maxndx = lhp->lh_chunksize / UMEM_BUFCTL_AUDIT_SIZE - 1;
   1575 
   1576 	if (mdb_vread(ulw->ulw_base, ulw->ulw_size,
   1577 	    (uintptr_t)lhp->lh_base) == -1) {
   1578 		mdb_warn("failed to read log at base %p", lhp->lh_base);
   1579 		mdb_free(ulw->ulw_base, ulw->ulw_size);
   1580 		mdb_free(ulw, sizeof (umem_log_walk_t));
   1581 		return (WALK_ERR);
   1582 	}
   1583 
   1584 	ulw->ulw_sorted = mdb_alloc(maxndx * lhp->lh_nchunks *
   1585 	    sizeof (umem_bufctl_audit_t *), UM_SLEEP);
   1586 
   1587 	for (i = 0, k = 0; i < lhp->lh_nchunks; i++) {
   1588 		caddr_t chunk = (caddr_t)
   1589 		    ((uintptr_t)ulw->ulw_base + i * lhp->lh_chunksize);
   1590 
   1591 		for (j = 0; j < maxndx; j++) {
   1592 			/* LINTED align */
   1593 			ulw->ulw_sorted[k++] = (umem_bufctl_audit_t *)chunk;
   1594 			chunk += UMEM_BUFCTL_AUDIT_SIZE;
   1595 		}
   1596 	}
   1597 
   1598 	qsort(ulw->ulw_sorted, k, sizeof (umem_bufctl_audit_t *),
   1599 	    (int(*)(const void *, const void *))bufctlcmp);
   1600 
   1601 	ulw->ulw_maxndx = k;
   1602 	wsp->walk_data = ulw;
   1603 
   1604 	return (WALK_NEXT);
   1605 }
   1606 
   1607 int
   1608 umem_log_walk_step(mdb_walk_state_t *wsp)
   1609 {
   1610 	umem_log_walk_t *ulw = wsp->walk_data;
   1611 	umem_bufctl_audit_t *bcp;
   1612 
   1613 	if (ulw->ulw_ndx == ulw->ulw_maxndx)
   1614 		return (WALK_DONE);
   1615 
   1616 	bcp = ulw->ulw_sorted[ulw->ulw_ndx++];
   1617 
   1618 	return (wsp->walk_callback((uintptr_t)bcp - (uintptr_t)ulw->ulw_base +
   1619 	    (uintptr_t)ulw->ulw_lh.lh_base, bcp, wsp->walk_cbdata));
   1620 }
   1621 
   1622 void
   1623 umem_log_walk_fini(mdb_walk_state_t *wsp)
   1624 {
   1625 	umem_log_walk_t *ulw = wsp->walk_data;
   1626 
   1627 	mdb_free(ulw->ulw_base, ulw->ulw_size);
   1628 	mdb_free(ulw->ulw_sorted, ulw->ulw_maxndx *
   1629 	    sizeof (umem_bufctl_audit_t *));
   1630 	mdb_free(ulw, sizeof (umem_log_walk_t));
   1631 }
   1632 
   1633 typedef struct allocdby_bufctl {
   1634 	uintptr_t abb_addr;
   1635 	hrtime_t abb_ts;
   1636 } allocdby_bufctl_t;
   1637 
   1638 typedef struct allocdby_walk {
   1639 	const char *abw_walk;
   1640 	uintptr_t abw_thread;
   1641 	size_t abw_nbufs;
   1642 	size_t abw_size;
   1643 	allocdby_bufctl_t *abw_buf;
   1644 	size_t abw_ndx;
   1645 } allocdby_walk_t;
   1646 
   1647 int
   1648 allocdby_walk_bufctl(uintptr_t addr, const umem_bufctl_audit_t *bcp,
   1649     allocdby_walk_t *abw)
   1650 {
   1651 	if ((uintptr_t)bcp->bc_thread != abw->abw_thread)
   1652 		return (WALK_NEXT);
   1653 
   1654 	if (abw->abw_nbufs == abw->abw_size) {
   1655 		allocdby_bufctl_t *buf;
   1656 		size_t oldsize = sizeof (allocdby_bufctl_t) * abw->abw_size;
   1657 
   1658 		buf = mdb_zalloc(oldsize << 1, UM_SLEEP);
   1659 
   1660 		bcopy(abw->abw_buf, buf, oldsize);
   1661 		mdb_free(abw->abw_buf, oldsize);
   1662 
   1663 		abw->abw_size <<= 1;
   1664 		abw->abw_buf = buf;
   1665 	}
   1666 
   1667 	abw->abw_buf[abw->abw_nbufs].abb_addr = addr;
   1668 	abw->abw_buf[abw->abw_nbufs].abb_ts = bcp->bc_timestamp;
   1669 	abw->abw_nbufs++;
   1670 
   1671 	return (WALK_NEXT);
   1672 }
   1673 
   1674 /*ARGSUSED*/
   1675 int
   1676 allocdby_walk_cache(uintptr_t addr, const umem_cache_t *c, allocdby_walk_t *abw)
   1677 {
   1678 	if (mdb_pwalk(abw->abw_walk, (mdb_walk_cb_t)allocdby_walk_bufctl,
   1679 	    abw, addr) == -1) {
   1680 		mdb_warn("couldn't walk bufctl for cache %p", addr);
   1681 		return (WALK_DONE);
   1682 	}
   1683 
   1684 	return (WALK_NEXT);
   1685 }
   1686 
   1687 static int
   1688 allocdby_cmp(const allocdby_bufctl_t *lhs, const allocdby_bufctl_t *rhs)
   1689 {
   1690 	if (lhs->abb_ts < rhs->abb_ts)
   1691 		return (1);
   1692 	if (lhs->abb_ts > rhs->abb_ts)
   1693 		return (-1);
   1694 	return (0);
   1695 }
   1696 
   1697 static int
   1698 allocdby_walk_init_common(mdb_walk_state_t *wsp, const char *walk)
   1699 {
   1700 	allocdby_walk_t *abw;
   1701 
   1702 	if (wsp->walk_addr == NULL) {
   1703 		mdb_warn("allocdby walk doesn't support global walks\n");
   1704 		return (WALK_ERR);
   1705 	}
   1706 
   1707 	abw = mdb_zalloc(sizeof (allocdby_walk_t), UM_SLEEP);
   1708 
   1709 	abw->abw_thread = wsp->walk_addr;
   1710 	abw->abw_walk = walk;
   1711 	abw->abw_size = 128;	/* something reasonable */
   1712 	abw->abw_buf =
   1713 	    mdb_zalloc(abw->abw_size * sizeof (allocdby_bufctl_t), UM_SLEEP);
   1714 
   1715 	wsp->walk_data = abw;
   1716 
   1717 	if (mdb_walk("umem_cache",
   1718 	    (mdb_walk_cb_t)allocdby_walk_cache, abw) == -1) {
   1719 		mdb_warn("couldn't walk umem_cache");
   1720 		allocdby_walk_fini(wsp);
   1721 		return (WALK_ERR);
   1722 	}
   1723 
   1724 	qsort(abw->abw_buf, abw->abw_nbufs, sizeof (allocdby_bufctl_t),
   1725 	    (int(*)(const void *, const void *))allocdby_cmp);
   1726 
   1727 	return (WALK_NEXT);
   1728 }
   1729 
   1730 int
   1731 allocdby_walk_init(mdb_walk_state_t *wsp)
   1732 {
   1733 	return (allocdby_walk_init_common(wsp, "bufctl"));
   1734 }
   1735 
   1736 int
   1737 freedby_walk_init(mdb_walk_state_t *wsp)
   1738 {
   1739 	return (allocdby_walk_init_common(wsp, "freectl"));
   1740 }
   1741 
   1742 int
   1743 allocdby_walk_step(mdb_walk_state_t *wsp)
   1744 {
   1745 	allocdby_walk_t *abw = wsp->walk_data;
   1746 	uintptr_t addr;
   1747 	umem_bufctl_audit_t *bcp;
   1748 	UMEM_LOCAL_BUFCTL_AUDIT(&bcp);
   1749 
   1750 	if (abw->abw_ndx == abw->abw_nbufs)
   1751 		return (WALK_DONE);
   1752 
   1753 	addr = abw->abw_buf[abw->abw_ndx++].abb_addr;
   1754 
   1755 	if (mdb_vread(bcp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) {
   1756 		mdb_warn("couldn't read bufctl at %p", addr);
   1757 		return (WALK_DONE);
   1758 	}
   1759 
   1760 	return (wsp->walk_callback(addr, bcp, wsp->walk_cbdata));
   1761 }
   1762 
   1763 void
   1764 allocdby_walk_fini(mdb_walk_state_t *wsp)
   1765 {
   1766 	allocdby_walk_t *abw = wsp->walk_data;
   1767 
   1768 	mdb_free(abw->abw_buf, sizeof (allocdby_bufctl_t) * abw->abw_size);
   1769 	mdb_free(abw, sizeof (allocdby_walk_t));
   1770 }
   1771 
   1772 /*ARGSUSED*/
   1773 int
   1774 allocdby_walk(uintptr_t addr, const umem_bufctl_audit_t *bcp, void *ignored)
   1775 {
   1776 	char c[MDB_SYM_NAMLEN];
   1777 	GElf_Sym sym;
   1778 	int i;
   1779 
   1780 	mdb_printf("%0?p %12llx ", addr, bcp->bc_timestamp);
   1781 	for (i = 0; i < bcp->bc_depth; i++) {
   1782 		if (mdb_lookup_by_addr(bcp->bc_stack[i],
   1783 		    MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1)
   1784 			continue;
   1785 		if (is_umem_sym(c, "umem_"))
   1786 			continue;
   1787 		mdb_printf("%s+0x%lx",
   1788 		    c, bcp->bc_stack[i] - (uintptr_t)sym.st_value);
   1789 		break;
   1790 	}
   1791 	mdb_printf("\n");
   1792 
   1793 	return (WALK_NEXT);
   1794 }
   1795 
   1796 static int
   1797 allocdby_common(uintptr_t addr, uint_t flags, const char *w)
   1798 {
   1799 	if (!(flags & DCMD_ADDRSPEC))
   1800 		return (DCMD_USAGE);
   1801 
   1802 	mdb_printf("%-?s %12s %s\n", "BUFCTL", "TIMESTAMP", "CALLER");
   1803 
   1804 	if (mdb_pwalk(w, (mdb_walk_cb_t)allocdby_walk, NULL, addr) == -1) {
   1805 		mdb_warn("can't walk '%s' for %p", w, addr);
   1806 		return (DCMD_ERR);
   1807 	}
   1808 
   1809 	return (DCMD_OK);
   1810 }
   1811 
   1812 /*ARGSUSED*/
   1813 int
   1814 allocdby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   1815 {
   1816 	return (allocdby_common(addr, flags, "allocdby"));
   1817 }
   1818 
   1819 /*ARGSUSED*/
   1820 int
   1821 freedby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   1822 {
   1823 	return (allocdby_common(addr, flags, "freedby"));
   1824 }
   1825 
   1826 typedef struct whatis_info {
   1827 	mdb_whatis_t *wi_w;
   1828 	const umem_cache_t *wi_cache;
   1829 	const vmem_t *wi_vmem;
   1830 	vmem_t *wi_msb_arena;
   1831 	size_t wi_slab_size;
   1832 	int wi_slab_found;
   1833 	uint_t wi_freemem;
   1834 } whatis_info_t;
   1835 
   1836 /* call one of our dcmd functions with "-v" and the provided address */
   1837 static void
   1838 whatis_call_printer(mdb_dcmd_f *dcmd, uintptr_t addr)
   1839 {
   1840 	mdb_arg_t a;
   1841 	a.a_type = MDB_TYPE_STRING;
   1842 	a.a_un.a_str = "-v";
   1843 
   1844 	mdb_printf(":\n");
   1845 	(void) (*dcmd)(addr, DCMD_ADDRSPEC, 1, &a);
   1846 }
   1847 
   1848 static void
   1849 whatis_print_umem(whatis_info_t *wi, uintptr_t maddr, uintptr_t addr,
   1850     uintptr_t baddr)
   1851 {
   1852 	mdb_whatis_t *w = wi->wi_w;
   1853 	const umem_cache_t *cp = wi->wi_cache;
   1854 	int quiet = (mdb_whatis_flags(w) & WHATIS_QUIET);
   1855 
   1856 	int call_printer = (!quiet && (cp->cache_flags & UMF_AUDIT));
   1857 
   1858 	mdb_whatis_report_object(w, maddr, addr, "");
   1859 
   1860 	if (baddr != 0 && !call_printer)
   1861 		mdb_printf("bufctl %p ", baddr);
   1862 
   1863 	mdb_printf("%s from %s",
   1864 	    (wi->wi_freemem == FALSE) ? "allocated" : "freed", cp->cache_name);
   1865 
   1866 	if (call_printer && baddr != 0) {
   1867 		whatis_call_printer(bufctl, baddr);
   1868 		return;
   1869 	}
   1870 	mdb_printf("\n");
   1871 }
   1872 
   1873 /*ARGSUSED*/
   1874 static int
   1875 whatis_walk_umem(uintptr_t addr, void *ignored, whatis_info_t *wi)
   1876 {
   1877 	mdb_whatis_t *w = wi->wi_w;
   1878 
   1879 	uintptr_t cur;
   1880 	size_t size = wi->wi_cache->cache_bufsize;
   1881 
   1882 	while (mdb_whatis_match(w, addr, size, &cur))
   1883 		whatis_print_umem(wi, cur, addr, NULL);
   1884 
   1885 	return (WHATIS_WALKRET(w));
   1886 }
   1887 
   1888 /*ARGSUSED*/
   1889 static int
   1890 whatis_walk_bufctl(uintptr_t baddr, const umem_bufctl_t *bcp, whatis_info_t *wi)
   1891 {
   1892 	mdb_whatis_t *w = wi->wi_w;
   1893 
   1894 	uintptr_t cur;
   1895 	uintptr_t addr = (uintptr_t)bcp->bc_addr;
   1896 	size_t size = wi->wi_cache->cache_bufsize;
   1897 
   1898 	while (mdb_whatis_match(w, addr, size, &cur))
   1899 		whatis_print_umem(wi, cur, addr, baddr);
   1900 
   1901 	return (WHATIS_WALKRET(w));
   1902 }
   1903 
   1904 
   1905 static int
   1906 whatis_walk_seg(uintptr_t addr, const vmem_seg_t *vs, whatis_info_t *wi)
   1907 {
   1908 	mdb_whatis_t *w = wi->wi_w;
   1909 
   1910 	size_t size = vs->vs_end - vs->vs_start;
   1911 	uintptr_t cur;
   1912 
   1913 	/* We're not interested in anything but alloc and free segments */
   1914 	if (vs->vs_type != VMEM_ALLOC && vs->vs_type != VMEM_FREE)
   1915 		return (WALK_NEXT);
   1916 
   1917 	while (mdb_whatis_match(w, vs->vs_start, size, &cur)) {
   1918 		mdb_whatis_report_object(w, cur, vs->vs_start, "");
   1919 
   1920 		/*
   1921 		 * If we're not printing it seperately, provide the vmem_seg
   1922 		 * pointer if it has a stack trace.
   1923 		 */
   1924 		if ((mdb_whatis_flags(w) & WHATIS_QUIET) &&
   1925 		    ((mdb_whatis_flags(w) & WHATIS_BUFCTL) != 0 ||
   1926 		    (vs->vs_type == VMEM_ALLOC && vs->vs_depth != 0))) {
   1927 			mdb_printf("vmem_seg %p ", addr);
   1928 		}
   1929 
   1930 		mdb_printf("%s from %s vmem arena",
   1931 		    (vs->vs_type == VMEM_ALLOC) ? "allocated" : "freed",
   1932 		    wi->wi_vmem->vm_name);
   1933 
   1934 		if (!mdb_whatis_flags(w) & WHATIS_QUIET)
   1935 			whatis_call_printer(vmem_seg, addr);
   1936 		else
   1937 			mdb_printf("\n");
   1938 	}
   1939 
   1940 	return (WHATIS_WALKRET(w));
   1941 }
   1942 
   1943 static int
   1944 whatis_walk_vmem(uintptr_t addr, const vmem_t *vmem, whatis_info_t *wi)
   1945 {
   1946 	mdb_whatis_t *w = wi->wi_w;
   1947 	const char *nm = vmem->vm_name;
   1948 	wi->wi_vmem = vmem;
   1949 
   1950 	if (mdb_whatis_flags(w) & WHATIS_VERBOSE)
   1951 		mdb_printf("Searching vmem arena %s...\n", nm);
   1952 
   1953 	if (mdb_pwalk("vmem_seg",
   1954 	    (mdb_walk_cb_t)whatis_walk_seg, wi, addr) == -1) {
   1955 		mdb_warn("can't walk vmem seg for %p", addr);
   1956 		return (WALK_NEXT);
   1957 	}
   1958 
   1959 	return (WHATIS_WALKRET(w));
   1960 }
   1961 
   1962 /*ARGSUSED*/
   1963 static int
   1964 whatis_walk_slab(uintptr_t saddr, const umem_slab_t *sp, whatis_info_t *wi)
   1965 {
   1966 	mdb_whatis_t *w = wi->wi_w;
   1967 
   1968 	/* It must overlap with the slab data, or it's not interesting */
   1969 	if (mdb_whatis_overlaps(w,
   1970 	    (uintptr_t)sp->slab_base, wi->wi_slab_size)) {
   1971 		wi->wi_slab_found++;
   1972 		return (WALK_DONE);
   1973 	}
   1974 	return (WALK_NEXT);
   1975 }
   1976 
   1977 static int
   1978 whatis_walk_cache(uintptr_t addr, const umem_cache_t *c, whatis_info_t *wi)
   1979 {
   1980 	mdb_whatis_t *w = wi->wi_w;
   1981 	char *walk, *freewalk;
   1982 	mdb_walk_cb_t func;
   1983 	int do_bufctl;
   1984 
   1985 	/* Override the '-b' flag as necessary */
   1986 	if (!(c->cache_flags & UMF_HASH))
   1987 		do_bufctl = FALSE;	/* no bufctls to walk */
   1988 	else if (c->cache_flags & UMF_AUDIT)
   1989 		do_bufctl = TRUE;	/* we always want debugging info */
   1990 	else
   1991 		do_bufctl = ((mdb_whatis_flags(w) & WHATIS_BUFCTL) != 0);
   1992 
   1993 	if (do_bufctl) {
   1994 		walk = "bufctl";
   1995 		freewalk = "freectl";
   1996 		func = (mdb_walk_cb_t)whatis_walk_bufctl;
   1997 	} else {
   1998 		walk = "umem";
   1999 		freewalk = "freemem";
   2000 		func = (mdb_walk_cb_t)whatis_walk_umem;
   2001 	}
   2002 
   2003 	wi->wi_cache = c;
   2004 
   2005 	if (mdb_whatis_flags(w) & WHATIS_VERBOSE)
   2006 		mdb_printf("Searching %s...\n", c->cache_name);
   2007 
   2008 	/*
   2009 	 * If more then two buffers live on each slab, figure out if we're
   2010 	 * interested in anything in any slab before doing the more expensive
   2011 	 * umem/freemem (bufctl/freectl) walkers.
   2012 	 */
   2013 	wi->wi_slab_size = c->cache_slabsize - c->cache_maxcolor;
   2014 	if (!(c->cache_flags & UMF_HASH))
   2015 		wi->wi_slab_size -= sizeof (umem_slab_t);
   2016 
   2017 	if ((wi->wi_slab_size / c->cache_chunksize) > 2) {
   2018 		wi->wi_slab_found = 0;
   2019 		if (mdb_pwalk("umem_slab", (mdb_walk_cb_t)whatis_walk_slab, wi,
   2020 		    addr) == -1) {
   2021 			mdb_warn("can't find umem_slab walker");
   2022 			return (WALK_DONE);
   2023 		}
   2024 		if (wi->wi_slab_found == 0)
   2025 			return (WALK_NEXT);
   2026 	}
   2027 
   2028 	wi->wi_freemem = FALSE;
   2029 	if (mdb_pwalk(walk, func, wi, addr) == -1) {
   2030 		mdb_warn("can't find %s walker", walk);
   2031 		return (WALK_DONE);
   2032 	}
   2033 
   2034 	if (mdb_whatis_done(w))
   2035 		return (WALK_DONE);
   2036 
   2037 	/*
   2038 	 * We have searched for allocated memory; now search for freed memory.
   2039 	 */
   2040 	if (mdb_whatis_flags(w) & WHATIS_VERBOSE)
   2041 		mdb_printf("Searching %s for free memory...\n", c->cache_name);
   2042 
   2043 	wi->wi_freemem = TRUE;
   2044 
   2045 	if (mdb_pwalk(freewalk, func, wi, addr) == -1) {
   2046 		mdb_warn("can't find %s walker", freewalk);
   2047 		return (WALK_DONE);
   2048 	}
   2049 
   2050 	return (WHATIS_WALKRET(w));
   2051 }
   2052 
   2053 static int
   2054 whatis_walk_touch(uintptr_t addr, const umem_cache_t *c, whatis_info_t *wi)
   2055 {
   2056 	if (c->cache_arena == wi->wi_msb_arena ||
   2057 	    (c->cache_cflags & UMC_NOTOUCH))
   2058 		return (WALK_NEXT);
   2059 
   2060 	return (whatis_walk_cache(addr, c, wi));
   2061 }
   2062 
   2063 static int
   2064 whatis_walk_metadata(uintptr_t addr, const umem_cache_t *c, whatis_info_t *wi)
   2065 {
   2066 	if (c->cache_arena != wi->wi_msb_arena)
   2067 		return (WALK_NEXT);
   2068 
   2069 	return (whatis_walk_cache(addr, c, wi));
   2070 }
   2071 
   2072 static int
   2073 whatis_walk_notouch(uintptr_t addr, const umem_cache_t *c, whatis_info_t *wi)
   2074 {
   2075 	if (c->cache_arena == wi->wi_msb_arena ||
   2076 	    !(c->cache_cflags & UMC_NOTOUCH))
   2077 		return (WALK_NEXT);
   2078 
   2079 	return (whatis_walk_cache(addr, c, wi));
   2080 }
   2081 
   2082 /*ARGSUSED*/
   2083 static int
   2084 whatis_run_umem(mdb_whatis_t *w, void *ignored)
   2085 {
   2086 	whatis_info_t wi;
   2087 
   2088 	bzero(&wi, sizeof (wi));
   2089 	wi.wi_w = w;
   2090 
   2091 	/* umem's metadata is allocated from the umem_internal_arena */
   2092 	if (mdb_readvar(&wi.wi_msb_arena, "umem_internal_arena") == -1)
   2093 		mdb_warn("unable to readvar \"umem_internal_arena\"");
   2094 
   2095 	/*
   2096 	 * We process umem caches in the following order:
   2097 	 *
   2098 	 *	non-UMC_NOTOUCH, non-metadata	(typically the most interesting)
   2099 	 *	metadata			(can be huge with UMF_AUDIT)
   2100 	 *	UMC_NOTOUCH, non-metadata	(see umem_walk_all())
   2101 	 */
   2102 	if (mdb_walk("umem_cache", (mdb_walk_cb_t)whatis_walk_touch,
   2103 	    &wi) == -1 ||
   2104 	    mdb_walk("umem_cache", (mdb_walk_cb_t)whatis_walk_metadata,
   2105 	    &wi) == -1 ||
   2106 	    mdb_walk("umem_cache", (mdb_walk_cb_t)whatis_walk_notouch,
   2107 	    &wi) == -1) {
   2108 		mdb_warn("couldn't find umem_cache walker");
   2109 		return (1);
   2110 	}
   2111 	return (0);
   2112 }
   2113 
   2114 /*ARGSUSED*/
   2115 static int
   2116 whatis_run_vmem(mdb_whatis_t *w, void *ignored)
   2117 {
   2118 	whatis_info_t wi;
   2119 
   2120 	bzero(&wi, sizeof (wi));
   2121 	wi.wi_w = w;
   2122 
   2123 	if (mdb_walk("vmem_postfix",
   2124 	    (mdb_walk_cb_t)whatis_walk_vmem, &wi) == -1) {
   2125 		mdb_warn("couldn't find vmem_postfix walker");
   2126 		return (1);
   2127 	}
   2128 	return (0);
   2129 }
   2130 
   2131 int
   2132 umem_init(void)
   2133 {
   2134 	mdb_walker_t w = {
   2135 		"umem_cache", "walk list of umem caches", umem_cache_walk_init,
   2136 		umem_cache_walk_step, umem_cache_walk_fini
   2137 	};
   2138 
   2139 	if (mdb_add_walker(&w) == -1) {
   2140 		mdb_warn("failed to add umem_cache walker");
   2141 		return (-1);
   2142 	}
   2143 
   2144 	if (umem_update_variables() == -1)
   2145 		return (-1);
   2146 
   2147 	/* install a callback so that our variables are always up-to-date */
   2148 	(void) mdb_callback_add(MDB_CALLBACK_STCHG, umem_statechange_cb, NULL);
   2149 	umem_statechange_cb(NULL);
   2150 
   2151 	/*
   2152 	 * Register our ::whatis callbacks.
   2153 	 */
   2154 	mdb_whatis_register("umem", whatis_run_umem, NULL,
   2155 	    WHATIS_PRIO_ALLOCATOR, WHATIS_REG_NO_ID);
   2156 	mdb_whatis_register("vmem", whatis_run_vmem, NULL,
   2157 	    WHATIS_PRIO_ALLOCATOR, WHATIS_REG_NO_ID);
   2158 
   2159 	return (0);
   2160 }
   2161 
   2162 typedef struct umem_log_cpu {
   2163 	uintptr_t umc_low;
   2164 	uintptr_t umc_high;
   2165 } umem_log_cpu_t;
   2166 
   2167 int
   2168 umem_log_walk(uintptr_t addr, const umem_bufctl_audit_t *b, umem_log_cpu_t *umc)
   2169 {
   2170 	int i;
   2171 
   2172 	for (i = 0; i < umem_max_ncpus; i++) {
   2173 		if (addr >= umc[i].umc_low && addr < umc[i].umc_high)
   2174 			break;
   2175 	}
   2176 
   2177 	if (i == umem_max_ncpus)
   2178 		mdb_printf("   ");
   2179 	else
   2180 		mdb_printf("%3d", i);
   2181 
   2182 	mdb_printf(" %0?p %0?p %16llx %0?p\n", addr, b->bc_addr,
   2183 	    b->bc_timestamp, b->bc_thread);
   2184 
   2185 	return (WALK_NEXT);
   2186 }
   2187 
   2188 /*ARGSUSED*/
   2189 int
   2190 umem_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   2191 {
   2192 	umem_log_header_t lh;
   2193 	umem_cpu_log_header_t clh;
   2194 	uintptr_t lhp, clhp;
   2195 	umem_log_cpu_t *umc;
   2196 	int i;
   2197 
   2198 	if (umem_readvar(&lhp, "umem_transaction_log") == -1) {
   2199 		mdb_warn("failed to read 'umem_transaction_log'");
   2200 		return (DCMD_ERR);
   2201 	}
   2202 
   2203 	if (lhp == NULL) {
   2204 		mdb_warn("no umem transaction log\n");
   2205 		return (DCMD_ERR);
   2206 	}
   2207 
   2208 	if (mdb_vread(&lh, sizeof (umem_log_header_t), lhp) == -1) {
   2209 		mdb_warn("failed to read log header at %p", lhp);
   2210 		return (DCMD_ERR);
   2211 	}
   2212 
   2213 	clhp = lhp + ((uintptr_t)&lh.lh_cpu[0] - (uintptr_t)&lh);
   2214 
   2215 	umc = mdb_zalloc(sizeof (umem_log_cpu_t) * umem_max_ncpus,
   2216 	    UM_SLEEP | UM_GC);
   2217 
   2218 	for (i = 0; i < umem_max_ncpus; i++) {
   2219 		if (mdb_vread(&clh, sizeof (clh), clhp) == -1) {
   2220 			mdb_warn("cannot read cpu %d's log header at %p",
   2221 			    i, clhp);
   2222 			return (DCMD_ERR);
   2223 		}
   2224 
   2225 		umc[i].umc_low = clh.clh_chunk * lh.lh_chunksize +
   2226 		    (uintptr_t)lh.lh_base;
   2227 		umc[i].umc_high = (uintptr_t)clh.clh_current;
   2228 
   2229 		clhp += sizeof (umem_cpu_log_header_t);
   2230 	}
   2231 
   2232 	if (DCMD_HDRSPEC(flags)) {
   2233 		mdb_printf("%3s %-?s %-?s %16s %-?s\n", "CPU", "ADDR",
   2234 		    "BUFADDR", "TIMESTAMP", "THREAD");
   2235 	}
   2236 
   2237 	/*
   2238 	 * If we have been passed an address, we'll just print out that
   2239 	 * log entry.
   2240 	 */
   2241 	if (flags & DCMD_ADDRSPEC) {
   2242 		umem_bufctl_audit_t *bp;
   2243 		UMEM_LOCAL_BUFCTL_AUDIT(&bp);
   2244 
   2245 		if (mdb_vread(bp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) {
   2246 			mdb_warn("failed to read bufctl at %p", addr);
   2247 			return (DCMD_ERR);
   2248 		}
   2249 
   2250 		(void) umem_log_walk(addr, bp, umc);
   2251 
   2252 		return (DCMD_OK);
   2253 	}
   2254 
   2255 	if (mdb_walk("umem_log", (mdb_walk_cb_t)umem_log_walk, umc) == -1) {
   2256 		mdb_warn("can't find umem log walker");
   2257 		return (DCMD_ERR);
   2258 	}
   2259 
   2260 	return (DCMD_OK);
   2261 }
   2262 
   2263 typedef struct bufctl_history_cb {
   2264 	int		bhc_flags;
   2265 	int		bhc_argc;
   2266 	const mdb_arg_t	*bhc_argv;
   2267 	int		bhc_ret;
   2268 } bufctl_history_cb_t;
   2269 
   2270 /*ARGSUSED*/
   2271 static int
   2272 bufctl_history_callback(uintptr_t addr, const void *ign, void *arg)
   2273 {
   2274 	bufctl_history_cb_t *bhc = arg;
   2275 
   2276 	bhc->bhc_ret =
   2277 	    bufctl(addr, bhc->bhc_flags, bhc->bhc_argc, bhc->bhc_argv);
   2278 
   2279 	bhc->bhc_flags &= ~DCMD_LOOPFIRST;
   2280 
   2281 	return ((bhc->bhc_ret == DCMD_OK)? WALK_NEXT : WALK_DONE);
   2282 }
   2283 
   2284 void
   2285 bufctl_help(void)
   2286 {
   2287 	mdb_printf("%s\n",
   2288 "Display the contents of umem_bufctl_audit_ts, with optional filtering.\n");
   2289 	mdb_dec_indent(2);
   2290 	mdb_printf("%<b>OPTIONS%</b>\n");
   2291 	mdb_inc_indent(2);
   2292 	mdb_printf("%s",
   2293 "  -v    Display the full content of the bufctl, including its stack trace\n"
   2294 "  -h    retrieve the bufctl's transaction history, if available\n"
   2295 "  -a addr\n"
   2296 "        filter out bufctls not involving the buffer at addr\n"
   2297 "  -c caller\n"
   2298 "        filter out bufctls without the function/PC in their stack trace\n"
   2299 "  -e earliest\n"
   2300 "        filter out bufctls timestamped before earliest\n"
   2301 "  -l latest\n"
   2302 "        filter out bufctls timestamped after latest\n"
   2303 "  -t thread\n"
   2304 "        filter out bufctls not involving thread\n");
   2305 }
   2306 
   2307 int
   2308 bufctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   2309 {
   2310 	uint_t verbose = FALSE;
   2311 	uint_t history = FALSE;
   2312 	uint_t in_history = FALSE;
   2313 	uintptr_t caller = NULL, thread = NULL;
   2314 	uintptr_t laddr, haddr, baddr = NULL;
   2315 	hrtime_t earliest = 0, latest = 0;
   2316 	int i, depth;
   2317 	char c[MDB_SYM_NAMLEN];
   2318 	GElf_Sym sym;
   2319 	umem_bufctl_audit_t *bcp;
   2320 	UMEM_LOCAL_BUFCTL_AUDIT(&bcp);
   2321 
   2322 	if (mdb_getopts(argc, argv,
   2323 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
   2324 	    'h', MDB_OPT_SETBITS, TRUE, &history,
   2325 	    'H', MDB_OPT_SETBITS, TRUE, &in_history,		/* internal */
   2326 	    'c', MDB_OPT_UINTPTR, &caller,
   2327 	    't', MDB_OPT_UINTPTR, &thread,
   2328 	    'e', MDB_OPT_UINT64, &earliest,
   2329 	    'l', MDB_OPT_UINT64, &latest,
   2330 	    'a', MDB_OPT_UINTPTR, &baddr, NULL) != argc)
   2331 		return (DCMD_USAGE);
   2332 
   2333 	if (!(flags & DCMD_ADDRSPEC))
   2334 		return (DCMD_USAGE);
   2335 
   2336 	if (in_history && !history)
   2337 		return (DCMD_USAGE);
   2338 
   2339 	if (history && !in_history) {
   2340 		mdb_arg_t *nargv = mdb_zalloc(sizeof (*nargv) * (argc + 1),
   2341 		    UM_SLEEP | UM_GC);
   2342 		bufctl_history_cb_t bhc;
   2343 
   2344 		nargv[0].a_type = MDB_TYPE_STRING;
   2345 		nargv[0].a_un.a_str = "-H";		/* prevent recursion */
   2346 
   2347 		for (i = 0; i < argc; i++)
   2348 			nargv[i + 1] = argv[i];
   2349 
   2350 		/*
   2351 		 * When in history mode, we treat each element as if it
   2352 		 * were in a seperate loop, so that the headers group
   2353 		 * bufctls with similar histories.
   2354 		 */
   2355 		bhc.bhc_flags = flags | DCMD_LOOP | DCMD_LOOPFIRST;
   2356 		bhc.bhc_argc = argc + 1;
   2357 		bhc.bhc_argv = nargv;
   2358 		bhc.bhc_ret = DCMD_OK;
   2359 
   2360 		if (mdb_pwalk("bufctl_history", bufctl_history_callback, &bhc,
   2361 		    addr) == -1) {
   2362 			mdb_warn("unable to walk bufctl_history");
   2363 			return (DCMD_ERR);
   2364 		}
   2365 
   2366 		if (bhc.bhc_ret == DCMD_OK && !(flags & DCMD_PIPE_OUT))
   2367 			mdb_printf("\n");
   2368 
   2369 		return (bhc.bhc_ret);
   2370 	}
   2371 
   2372 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
   2373 		if (verbose) {
   2374 			mdb_printf("%16s %16s %16s %16s\n"
   2375 			    "%<u>%16s %16s %16s %16s%</u>\n",
   2376 			    "ADDR", "BUFADDR", "TIMESTAMP", "THREAD",
   2377 			    "", "CACHE", "LASTLOG", "CONTENTS");
   2378 		} else {
   2379 			mdb_printf("%<u>%-?s %-?s %-12s %5s %s%</u>\n",
   2380 			    "ADDR", "BUFADDR", "TIMESTAMP", "THRD", "CALLER");
   2381 		}
   2382 	}
   2383 
   2384 	if (mdb_vread(bcp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) {
   2385 		mdb_warn("couldn't read bufctl at %p", addr);
   2386 		return (DCMD_ERR);
   2387 	}
   2388 
   2389 	/*
   2390 	 * Guard against bogus bc_depth in case the bufctl is corrupt or
   2391 	 * the address does not really refer to a bufctl.
   2392 	 */
   2393 	depth = MIN(bcp->bc_depth, umem_stack_depth);
   2394 
   2395 	if (caller != NULL) {
   2396 		laddr = caller;
   2397 		haddr = caller + sizeof (caller);
   2398 
   2399 		if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c, sizeof (c),
   2400 		    &sym) != -1 && caller == (uintptr_t)sym.st_value) {
   2401 			/*
   2402 			 * We were provided an exact symbol value; any
   2403 			 * address in the function is valid.
   2404 			 */
   2405 			laddr = (uintptr_t)sym.st_value;
   2406 			haddr = (uintptr_t)sym.st_value + sym.st_size;
   2407 		}
   2408 
   2409 		for (i = 0; i < depth; i++)
   2410 			if (bcp->bc_stack[i] >= laddr &&
   2411 			    bcp->bc_stack[i] < haddr)
   2412 				break;
   2413 
   2414 		if (i == depth)
   2415 			return (DCMD_OK);
   2416 	}
   2417 
   2418 	if (thread != NULL && (uintptr_t)bcp->bc_thread != thread)
   2419 		return (DCMD_OK);
   2420 
   2421 	if (earliest != 0 && bcp->bc_timestamp < earliest)
   2422 		return (DCMD_OK);
   2423 
   2424 	if (latest != 0 && bcp->bc_timestamp > latest)
   2425 		return (DCMD_OK);
   2426 
   2427 	if (baddr != 0 && (uintptr_t)bcp->bc_addr != baddr)
   2428 		return (DCMD_OK);
   2429 
   2430 	if (flags & DCMD_PIPE_OUT) {
   2431 		mdb_printf("%#r\n", addr);
   2432 		return (DCMD_OK);
   2433 	}
   2434 
   2435 	if (verbose) {
   2436 		mdb_printf(
   2437 		    "%<b>%16p%</b> %16p %16llx %16d\n"
   2438 		    "%16s %16p %16p %16p\n",
   2439 		    addr, bcp->bc_addr, bcp->bc_timestamp, bcp->bc_thread,
   2440 		    "", bcp->bc_cache, bcp->bc_lastlog, bcp->bc_contents);
   2441 
   2442 		mdb_inc_indent(17);
   2443 		for (i = 0; i < depth; i++)
   2444 			mdb_printf("%a\n", bcp->bc_stack[i]);
   2445 		mdb_dec_indent(17);
   2446 		mdb_printf("\n");
   2447 	} else {
   2448 		mdb_printf("%0?p %0?p %12llx %5d", addr, bcp->bc_addr,
   2449 		    bcp->bc_timestamp, bcp->bc_thread);
   2450 
   2451 		for (i = 0; i < depth; i++) {
   2452 			if (mdb_lookup_by_addr(bcp->bc_stack[i],
   2453 			    MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1)
   2454 				continue;
   2455 			if (is_umem_sym(c, "umem_"))
   2456 				continue;
   2457 			mdb_printf(" %a\n", bcp->bc_stack[i]);
   2458 			break;
   2459 		}
   2460 
   2461 		if (i >= depth)
   2462 			mdb_printf("\n");
   2463 	}
   2464 
   2465 	return (DCMD_OK);
   2466 }
   2467 
   2468 /*ARGSUSED*/
   2469 int
   2470 bufctl_audit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   2471 {
   2472 	mdb_arg_t a;
   2473 
   2474 	if (!(flags & DCMD_ADDRSPEC))
   2475 		return (DCMD_USAGE);
   2476 
   2477 	if (argc != 0)
   2478 		return (DCMD_USAGE);
   2479 
   2480 	a.a_type = MDB_TYPE_STRING;
   2481 	a.a_un.a_str = "-v";
   2482 
   2483 	return (bufctl(addr, flags, 1, &a));
   2484 }
   2485 
   2486 typedef struct umem_verify {
   2487 	uint64_t *umv_buf;		/* buffer to read cache contents into */
   2488 	size_t umv_size;		/* number of bytes in umv_buf */
   2489 	int umv_corruption;		/* > 0 if corruption found. */
   2490 	int umv_besilent;		/* report actual corruption sites */
   2491 	struct umem_cache umv_cache;	/* the cache we're operating on */
   2492 } umem_verify_t;
   2493 
   2494 /*
   2495  * verify_pattern()
   2496  *	verify that buf is filled with the pattern pat.
   2497  */
   2498 static int64_t
   2499 verify_pattern(uint64_t *buf_arg, size_t size, uint64_t pat)
   2500 {
   2501 	/*LINTED*/
   2502 	uint64_t *bufend = (uint64_t *)((char *)buf_arg + size);
   2503 	uint64_t *buf;
   2504 
   2505 	for (buf = buf_arg; buf < bufend; buf++)
   2506 		if (*buf != pat)
   2507 			return ((uintptr_t)buf - (uintptr_t)buf_arg);
   2508 	return (-1);
   2509 }
   2510 
   2511 /*
   2512  * verify_buftag()
   2513  *	verify that btp->bt_bxstat == (bcp ^ pat)
   2514  */
   2515 static int
   2516 verify_buftag(umem_buftag_t *btp, uintptr_t pat)
   2517 {
   2518 	return (btp->bt_bxstat == ((intptr_t)btp->bt_bufctl ^ pat) ? 0 : -1);
   2519 }
   2520 
   2521 /*
   2522  * verify_free()
   2523  *	verify the integrity of a free block of memory by checking
   2524  *	that it is filled with 0xdeadbeef and that its buftag is sane.
   2525  */
   2526 /*ARGSUSED1*/
   2527 static int
   2528 verify_free(uintptr_t addr, const void *data, void *private)
   2529 {
   2530 	umem_verify_t *umv = (umem_verify_t *)private;
   2531 	uint64_t *buf = umv->umv_buf;	/* buf to validate */
   2532 	int64_t corrupt;		/* corruption offset */
   2533 	umem_buftag_t *buftagp;		/* ptr to buftag */
   2534 	umem_cache_t *cp = &umv->umv_cache;
   2535 	int besilent = umv->umv_besilent;
   2536 
   2537 	/*LINTED*/
   2538 	buftagp = UMEM_BUFTAG(cp, buf);
   2539 
   2540 	/*
   2541 	 * Read the buffer to check.
   2542 	 */
   2543 	if (mdb_vread(buf, umv->umv_size, addr) == -1) {
   2544 		if (!besilent)
   2545 			mdb_warn("couldn't read %p", addr);
   2546 		return (WALK_NEXT);
   2547 	}
   2548 
   2549 	if ((corrupt = verify_pattern(buf, cp->cache_verify,
   2550 	    UMEM_FREE_PATTERN)) >= 0) {
   2551 		if (!besilent)
   2552 			mdb_printf("buffer %p (free) seems corrupted, at %p\n",
   2553 			    addr, (uintptr_t)addr + corrupt);
   2554 		goto corrupt;
   2555 	}
   2556 
   2557 	if ((cp->cache_flags & UMF_HASH) &&
   2558 	    buftagp->bt_redzone != UMEM_REDZONE_PATTERN) {
   2559 		if (!besilent)
   2560 			mdb_printf("buffer %p (free) seems to "
   2561 			    "have a corrupt redzone pattern\n", addr);
   2562 		goto corrupt;
   2563 	}
   2564 
   2565 	/*
   2566 	 * confirm bufctl pointer integrity.
   2567 	 */
   2568 	if (verify_buftag(buftagp, UMEM_BUFTAG_FREE) == -1) {
   2569 		if (!besilent)
   2570 			mdb_printf("buffer %p (free) has a corrupt "
   2571 			    "buftag\n", addr);
   2572 		goto corrupt;
   2573 	}
   2574 
   2575 	return (WALK_NEXT);
   2576 corrupt:
   2577 	umv->umv_corruption++;
   2578 	return (WALK_NEXT);
   2579 }
   2580 
   2581 /*
   2582  * verify_alloc()
   2583  *	Verify that the buftag of an allocated buffer makes sense with respect
   2584  *	to the buffer.
   2585  */
   2586 /*ARGSUSED1*/
   2587 static int
   2588 verify_alloc(uintptr_t addr, const void *data, void *private)
   2589 {
   2590 	umem_verify_t *umv = (umem_verify_t *)private;
   2591 	umem_cache_t *cp = &umv->umv_cache;
   2592 	uint64_t *buf = umv->umv_buf;	/* buf to validate */
   2593 	/*LINTED*/
   2594 	umem_buftag_t *buftagp = UMEM_BUFTAG(cp, buf);
   2595 	uint32_t *ip = (uint32_t *)buftagp;
   2596 	uint8_t *bp = (uint8_t *)buf;
   2597 	int looks_ok = 0, size_ok = 1;	/* flags for finding corruption */
   2598 	int besilent = umv->umv_besilent;
   2599 
   2600 	/*
   2601 	 * Read the buffer to check.
   2602 	 */
   2603 	if (mdb_vread(buf, umv->umv_size, addr) == -1) {
   2604 		if (!besilent)
   2605 			mdb_warn("couldn't read %p", addr);
   2606 		return (WALK_NEXT);
   2607 	}
   2608 
   2609 	/*
   2610 	 * There are two cases to handle:
   2611 	 * 1. If the buf was alloc'd using umem_cache_alloc, it will have
   2612 	 *    0xfeedfacefeedface at the end of it
   2613 	 * 2. If the buf was alloc'd using umem_alloc, it will have
   2614 	 *    0xbb just past the end of the region in use.  At the buftag,
   2615 	 *    it will have 0xfeedface (or, if the whole buffer is in use,
   2616 	 *    0xfeedface & bb000000 or 0xfeedfacf & 000000bb depending on
   2617 	 *    endianness), followed by 32 bits containing the offset of the
   2618 	 *    0xbb byte in the buffer.
   2619 	 *
   2620 	 * Finally, the two 32-bit words that comprise the second half of the
   2621 	 * buftag should xor to UMEM_BUFTAG_ALLOC
   2622 	 */
   2623 
   2624 	if (buftagp->bt_redzone == UMEM_REDZONE_PATTERN)
   2625 		looks_ok = 1;
   2626 	else if (!UMEM_SIZE_VALID(ip[1]))
   2627 		size_ok = 0;
   2628 	else if (bp[UMEM_SIZE_DECODE(ip[1])] == UMEM_REDZONE_BYTE)
   2629 		looks_ok = 1;
   2630 	else
   2631 		size_ok = 0;
   2632 
   2633 	if (!size_ok) {
   2634 		if (!besilent)
   2635 			mdb_printf("buffer %p (allocated) has a corrupt "
   2636 			    "redzone size encoding\n", addr);
   2637 		goto corrupt;
   2638 	}
   2639 
   2640 	if (!looks_ok) {
   2641 		if (!besilent)
   2642 			mdb_printf("buffer %p (allocated) has a corrupt "
   2643 			    "redzone signature\n", addr);
   2644 		goto corrupt;
   2645 	}
   2646 
   2647 	if (verify_buftag(buftagp, UMEM_BUFTAG_ALLOC) == -1) {
   2648 		if (!besilent)
   2649 			mdb_printf("buffer %p (allocated) has a "
   2650 			    "corrupt buftag\n", addr);
   2651 		goto corrupt;
   2652 	}
   2653 
   2654 	return (WALK_NEXT);
   2655 corrupt:
   2656 	umv->umv_corruption++;
   2657 	return (WALK_NEXT);
   2658 }
   2659 
   2660 /*ARGSUSED2*/
   2661 int
   2662 umem_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   2663 {
   2664 	if (flags & DCMD_ADDRSPEC) {
   2665 		int check_alloc = 0, check_free = 0;
   2666 		umem_verify_t umv;
   2667 
   2668 		if (mdb_vread(&umv.umv_cache, sizeof (umv.umv_cache),
   2669 		    addr) == -1) {
   2670 			mdb_warn("couldn't read umem_cache %p", addr);
   2671 			return (DCMD_ERR);
   2672 		}
   2673 
   2674 		umv.umv_size = umv.umv_cache.cache_buftag +
   2675 		    sizeof (umem_buftag_t);
   2676 		umv.umv_buf = mdb_alloc(umv.umv_size, UM_SLEEP | UM_GC);
   2677 		umv.umv_corruption = 0;
   2678 
   2679 		if ((umv.umv_cache.cache_flags & UMF_REDZONE)) {
   2680 			check_alloc = 1;
   2681 			if (umv.umv_cache.cache_flags & UMF_DEADBEEF)
   2682 				check_free = 1;
   2683 		} else {
   2684 			if (!(flags & DCMD_LOOP)) {
   2685 				mdb_warn("cache %p (%s) does not have "
   2686 				    "redzone checking enabled\n", addr,
   2687 				    umv.umv_cache.cache_name);
   2688 			}
   2689 			return (DCMD_ERR);
   2690 		}
   2691 
   2692 		if (flags & DCMD_LOOP) {
   2693 			/*
   2694 			 * table mode, don't print out every corrupt buffer
   2695 			 */
   2696 			umv.umv_besilent = 1;
   2697 		} else {
   2698 			mdb_printf("Summary for cache '%s'\n",
   2699 			    umv.umv_cache.cache_name);
   2700 			mdb_inc_indent(2);
   2701 			umv.umv_besilent = 0;
   2702 		}
   2703 
   2704 		if (check_alloc)
   2705 			(void) mdb_pwalk("umem", verify_alloc, &umv, addr);
   2706 		if (check_free)
   2707 			(void) mdb_pwalk("freemem", verify_free, &umv, addr);
   2708 
   2709 		if (flags & DCMD_LOOP) {
   2710 			if (umv.umv_corruption == 0) {
   2711 				mdb_printf("%-*s %?p clean\n",
   2712 				    UMEM_CACHE_NAMELEN,
   2713 				    umv.umv_cache.cache_name, addr);
   2714 			} else {
   2715 				char *s = "";	/* optional s in "buffer[s]" */
   2716 				if (umv.umv_corruption > 1)
   2717 					s = "s";
   2718 
   2719 				mdb_printf("%-*s %?p %d corrupt buffer%s\n",
   2720 				    UMEM_CACHE_NAMELEN,
   2721 				    umv.umv_cache.cache_name, addr,
   2722 				    umv.umv_corruption, s);
   2723 			}
   2724 		} else {
   2725 			/*
   2726 			 * This is the more verbose mode, when the user has
   2727 			 * type addr::umem_verify.  If the cache was clean,
   2728 			 * nothing will have yet been printed. So say something.
   2729 			 */
   2730 			if (umv.umv_corruption == 0)
   2731 				mdb_printf("clean\n");
   2732 
   2733 			mdb_dec_indent(2);
   2734 		}
   2735 	} else {
   2736 		/*
   2737 		 * If the user didn't specify a cache to verify, we'll walk all
   2738 		 * umem_cache's, specifying ourself as a callback for each...
   2739 		 * this is the equivalent of '::walk umem_cache .::umem_verify'
   2740 		 */
   2741 		mdb_printf("%<u>%-*s %-?s %-20s%</b>\n", UMEM_CACHE_NAMELEN,
   2742 		    "Cache Name", "Addr", "Cache Integrity");
   2743 		(void) (mdb_walk_dcmd("umem_cache", "umem_verify", 0, NULL));
   2744 	}
   2745 
   2746 	return (DCMD_OK);
   2747 }
   2748 
   2749 typedef struct vmem_node {
   2750 	struct vmem_node *vn_next;
   2751 	struct vmem_node *vn_parent;
   2752 	struct vmem_node *vn_sibling;
   2753 	struct vmem_node *vn_children;
   2754 	uintptr_t vn_addr;
   2755 	int vn_marked;
   2756 	vmem_t vn_vmem;
   2757 } vmem_node_t;
   2758 
   2759 typedef struct vmem_walk {
   2760 	vmem_node_t *vw_root;
   2761 	vmem_node_t *vw_current;
   2762 } vmem_walk_t;
   2763 
   2764 int
   2765 vmem_walk_init(mdb_walk_state_t *wsp)
   2766 {
   2767 	uintptr_t vaddr, paddr;
   2768 	vmem_node_t *head = NULL, *root = NULL, *current = NULL, *parent, *vp;
   2769 	vmem_walk_t *vw;
   2770 
   2771 	if (umem_readvar(&vaddr, "vmem_list") == -1) {
   2772 		mdb_warn("couldn't read 'vmem_list'");
   2773 		return (WALK_ERR);
   2774 	}
   2775 
   2776 	while (vaddr != NULL) {
   2777 		vp = mdb_zalloc(sizeof (vmem_node_t), UM_SLEEP);
   2778 		vp->vn_addr = vaddr;
   2779 		vp->vn_next = head;
   2780 		head = vp;
   2781 
   2782 		if (vaddr == wsp->walk_addr)
   2783 			current = vp;
   2784 
   2785 		if (mdb_vread(&vp->vn_vmem, sizeof (vmem_t), vaddr) == -1) {
   2786 			mdb_warn("couldn't read vmem_t at %p", vaddr);
   2787 			goto err;
   2788 		}
   2789 
   2790 		vaddr = (uintptr_t)vp->vn_vmem.vm_next;
   2791 	}
   2792 
   2793 	for (vp = head; vp != NULL; vp = vp->vn_next) {
   2794 
   2795 		if ((paddr = (uintptr_t)vp->vn_vmem.vm_source) == NULL) {
   2796 			vp->vn_sibling = root;
   2797 			root = vp;
   2798 			continue;
   2799 		}
   2800 
   2801 		for (parent = head; parent != NULL; parent = parent->vn_next) {
   2802 			if (parent->vn_addr != paddr)
   2803 				continue;
   2804 			vp->vn_sibling = parent->vn_children;
   2805 			parent->vn_children = vp;
   2806 			vp->vn_parent = parent;
   2807 			break;
   2808 		}
   2809 
   2810 		if (parent == NULL) {
   2811 			mdb_warn("couldn't find %p's parent (%p)\n",
   2812 			    vp->vn_addr, paddr);
   2813 			goto err;
   2814 		}
   2815 	}
   2816 
   2817 	vw = mdb_zalloc(sizeof (vmem_walk_t), UM_SLEEP);
   2818 	vw->vw_root = root;
   2819 
   2820 	if (current != NULL)
   2821 		vw->vw_current = current;
   2822 	else
   2823 		vw->vw_current = root;
   2824 
   2825 	wsp->walk_data = vw;
   2826 	return (WALK_NEXT);
   2827 err:
   2828 	for (vp = head; head != NULL; vp = head) {
   2829 		head = vp->vn_next;
   2830 		mdb_free(vp, sizeof (vmem_node_t));
   2831 	}
   2832 
   2833 	return (WALK_ERR);
   2834 }
   2835 
   2836 int
   2837 vmem_walk_step(mdb_walk_state_t *wsp)
   2838 {
   2839 	vmem_walk_t *vw = wsp->walk_data;
   2840 	vmem_node_t *vp;
   2841 	int rval;
   2842 
   2843 	if ((vp = vw->vw_current) == NULL)
   2844 		return (WALK_DONE);
   2845 
   2846 	rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata);
   2847 
   2848 	if (vp->vn_children != NULL) {
   2849 		vw->vw_current = vp->vn_children;
   2850 		return (rval);
   2851 	}
   2852 
   2853 	do {
   2854 		vw->vw_current = vp->vn_sibling;
   2855 		vp = vp->vn_parent;
   2856 	} while (vw->vw_current == NULL && vp != NULL);
   2857 
   2858 	return (rval);
   2859 }
   2860 
   2861 /*
   2862  * The "vmem_postfix" walk walks the vmem arenas in post-fix order; all
   2863  * children are visited before their parent.  We perform the postfix walk
   2864  * iteratively (rather than recursively) to allow mdb to regain control
   2865  * after each callback.
   2866  */
   2867 int
   2868 vmem_postfix_walk_step(mdb_walk_state_t *wsp)
   2869 {
   2870 	vmem_walk_t *vw = wsp->walk_data;
   2871 	vmem_node_t *vp = vw->vw_current;
   2872 	int rval;
   2873 
   2874 	/*
   2875 	 * If this node is marked, then we know that we have already visited
   2876 	 * all of its children.  If the node has any siblings, they need to
   2877 	 * be visited next; otherwise, we need to visit the parent.  Note
   2878 	 * that vp->vn_marked will only be zero on the first invocation of
   2879 	 * the step function.
   2880 	 */
   2881 	if (vp->vn_marked) {
   2882 		if (vp->vn_sibling != NULL)
   2883 			vp = vp->vn_sibling;
   2884 		else if (vp->vn_parent != NULL)
   2885 			vp = vp->vn_parent;
   2886 		else {
   2887 			/*
   2888 			 * We have neither a parent, nor a sibling, and we
   2889 			 * have already been visited; we're done.
   2890 			 */
   2891 			return (WALK_DONE);
   2892 		}
   2893 	}
   2894 
   2895 	/*
   2896 	 * Before we visit this node, visit its children.
   2897 	 */
   2898 	while (vp->vn_children != NULL && !vp->vn_children->vn_marked)
   2899 		vp = vp->vn_children;
   2900 
   2901 	vp->vn_marked = 1;
   2902 	vw->vw_current = vp;
   2903 	rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata);
   2904 
   2905 	return (rval);
   2906 }
   2907 
   2908 void
   2909 vmem_walk_fini(mdb_walk_state_t *wsp)
   2910 {
   2911 	vmem_walk_t *vw = wsp->walk_data;
   2912 	vmem_node_t *root = vw->vw_root;
   2913 	int done;
   2914 
   2915 	if (root == NULL)
   2916 		return;
   2917 
   2918 	if ((vw->vw_root = root->vn_children) != NULL)
   2919 		vmem_walk_fini(wsp);
   2920 
   2921 	vw->vw_root = root->vn_sibling;
   2922 	done = (root->vn_sibling == NULL && root->vn_parent == NULL);
   2923 	mdb_free(root, sizeof (vmem_node_t));
   2924 
   2925 	if (done) {
   2926 		mdb_free(vw, sizeof (vmem_walk_t));
   2927 	} else {
   2928 		vmem_walk_fini(wsp);
   2929 	}
   2930 }
   2931 
   2932 typedef struct vmem_seg_walk {
   2933 	uint8_t vsw_type;
   2934 	uintptr_t vsw_start;
   2935 	uintptr_t vsw_current;
   2936 } vmem_seg_walk_t;
   2937 
   2938 /*ARGSUSED*/
   2939 int
   2940 vmem_seg_walk_common_init(mdb_walk_state_t *wsp, uint8_t type, char *name)
   2941 {
   2942 	vmem_seg_walk_t *vsw;
   2943 
   2944 	if (wsp->walk_addr == NULL) {
   2945 		mdb_warn("vmem_%s does not support global walks\n", name);
   2946 		return (WALK_ERR);
   2947 	}
   2948 
   2949 	wsp->walk_data = vsw = mdb_alloc(sizeof (vmem_seg_walk_t), UM_SLEEP);
   2950 
   2951 	vsw->vsw_type = type;
   2952 	vsw->vsw_start = wsp->walk_addr + OFFSETOF(vmem_t, vm_seg0);
   2953 	vsw->vsw_current = vsw->vsw_start;
   2954 
   2955 	return (WALK_NEXT);
   2956 }
   2957 
   2958 /*
   2959  * vmem segments can't have type 0 (this should be added to vmem_impl.h).
   2960  */
   2961 #define	VMEM_NONE	0
   2962 
   2963 int
   2964 vmem_alloc_walk_init(mdb_walk_state_t *wsp)
   2965 {
   2966 	return (vmem_seg_walk_common_init(wsp, VMEM_ALLOC, "alloc"));
   2967 }
   2968 
   2969 int
   2970 vmem_free_walk_init(mdb_walk_state_t *wsp)
   2971 {
   2972 	return (vmem_seg_walk_common_init(wsp, VMEM_FREE, "free"));
   2973 }
   2974 
   2975 int
   2976 vmem_span_walk_init(mdb_walk_state_t *wsp)
   2977 {
   2978 	return (vmem_seg_walk_common_init(wsp, VMEM_SPAN, "span"));
   2979 }
   2980 
   2981 int
   2982 vmem_seg_walk_init(mdb_walk_state_t *wsp)
   2983 {
   2984 	return (vmem_seg_walk_common_init(wsp, VMEM_NONE, "seg"));
   2985 }
   2986 
   2987 int
   2988 vmem_seg_walk_step(mdb_walk_state_t *wsp)
   2989 {
   2990 	vmem_seg_t seg;
   2991 	vmem_seg_walk_t *vsw = wsp->walk_data;
   2992 	uintptr_t addr = vsw->vsw_current;
   2993 	static size_t seg_size = 0;
   2994 	int rval;
   2995 
   2996 	if (!seg_size) {
   2997 		if (umem_readvar(&seg_size, "vmem_seg_size") == -1) {
   2998 			mdb_warn("failed to read 'vmem_seg_size'");
   2999 			seg_size = sizeof (vmem_seg_t);
   3000 		}
   3001 	}
   3002 
   3003 	if (seg_size < sizeof (seg))
   3004 		bzero((caddr_t)&seg + seg_size, sizeof (seg) - seg_size);
   3005 
   3006 	if (mdb_vread(&seg, seg_size, addr) == -1) {
   3007 		mdb_warn("couldn't read vmem_seg at %p", addr);
   3008 		return (WALK_ERR);
   3009 	}
   3010 
   3011 	vsw->vsw_current = (uintptr_t)seg.vs_anext;
   3012 	if (vsw->vsw_type != VMEM_NONE && seg.vs_type != vsw->vsw_type) {
   3013 		rval = WALK_NEXT;
   3014 	} else {
   3015 		rval = wsp->walk_callback(addr, &seg, wsp->walk_cbdata);
   3016 	}
   3017 
   3018 	if (vsw->vsw_current == vsw->vsw_start)
   3019 		return (WALK_DONE);
   3020 
   3021 	return (rval);
   3022 }
   3023 
   3024 void
   3025 vmem_seg_walk_fini(mdb_walk_state_t *wsp)
   3026 {
   3027 	vmem_seg_walk_t *vsw = wsp->walk_data;
   3028 
   3029 	mdb_free(vsw, sizeof (vmem_seg_walk_t));
   3030 }
   3031 
   3032 #define	VMEM_NAMEWIDTH	22
   3033 
   3034 int
   3035 vmem(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   3036 {
   3037 	vmem_t v, parent;
   3038 	uintptr_t paddr;
   3039 	int ident = 0;
   3040 	char c[VMEM_NAMEWIDTH];
   3041 
   3042 	if (!(flags & DCMD_ADDRSPEC)) {
   3043 		if (mdb_walk_dcmd("vmem", "vmem", argc, argv) == -1) {
   3044 			mdb_warn("can't walk vmem");
   3045 			return (DCMD_ERR);
   3046 		}
   3047 		return (DCMD_OK);
   3048 	}
   3049 
   3050 	if (DCMD_HDRSPEC(flags))
   3051 		mdb_printf("%-?s %-*s %10s %12s %9s %5s\n",
   3052 		    "ADDR", VMEM_NAMEWIDTH, "NAME", "INUSE",
   3053 		    "TOTAL", "SUCCEED", "FAIL");
   3054 
   3055 	if (mdb_vread(&v, sizeof (v), addr) == -1) {
   3056 		mdb_warn("couldn't read vmem at %p", addr);
   3057 		return (DCMD_ERR);
   3058 	}
   3059 
   3060 	for (paddr = (uintptr_t)v.vm_source; paddr != NULL; ident += 2) {
   3061 		if (mdb_vread(&parent, sizeof (parent), paddr) == -1) {
   3062 			mdb_warn("couldn't trace %p's ancestry", addr);
   3063 			ident = 0;
   3064 			break;
   3065 		}
   3066 		paddr = (uintptr_t)parent.vm_source;
   3067 	}
   3068 
   3069 	(void) mdb_snprintf(c, VMEM_NAMEWIDTH, "%*s%s", ident, "", v.vm_name);
   3070 
   3071 	mdb_printf("%0?p %-*s %10llu %12llu %9llu %5llu\n",
   3072 	    addr, VMEM_NAMEWIDTH, c,
   3073 	    v.vm_kstat.vk_mem_inuse, v.vm_kstat.vk_mem_total,
   3074 	    v.vm_kstat.vk_alloc, v.vm_kstat.vk_fail);
   3075 
   3076 	return (DCMD_OK);
   3077 }
   3078 
   3079 void
   3080 vmem_seg_help(void)
   3081 {
   3082 	mdb_printf("%s\n",
   3083 "Display the contents of vmem_seg_ts, with optional filtering.\n"
   3084 "\n"
   3085 "A vmem_seg_t represents a range of addresses (or arbitrary numbers),\n"
   3086 "representing a single chunk of data.  Only ALLOC segments have debugging\n"
   3087 "information.\n");
   3088 	mdb_dec_indent(2);
   3089 	mdb_printf("%<b>OPTIONS%</b>\n");
   3090 	mdb_inc_indent(2);
   3091 	mdb_printf("%s",
   3092 "  -v    Display the full content of the vmem_seg, including its stack trace\n"
   3093 "  -s    report the size of the segment, instead of the end address\n"
   3094 "  -c caller\n"
   3095 "        filter out segments without the function/PC in their stack trace\n"
   3096 "  -e earliest\n"
   3097 "        filter out segments timestamped before earliest\n"
   3098 "  -l latest\n"
   3099 "        filter out segments timestamped after latest\n"
   3100 "  -m minsize\n"
   3101 "        filer out segments smaller than minsize\n"
   3102 "  -M maxsize\n"
   3103 "        filer out segments larger than maxsize\n"
   3104 "  -t thread\n"
   3105 "        filter out segments not involving thread\n"
   3106 "  -T type\n"
   3107 "        filter out segments not of type 'type'\n"
   3108 "        type is one of: ALLOC/FREE/SPAN/ROTOR/WALKER\n");
   3109 }
   3110 
   3111 
   3112 /*ARGSUSED*/
   3113 int
   3114 vmem_seg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   3115 {
   3116 	vmem_seg_t vs;
   3117 	uintptr_t *stk = vs.vs_stack;
   3118 	uintptr_t sz;
   3119 	uint8_t t;
   3120 	const char *type = NULL;
   3121 	GElf_Sym sym;
   3122 	char c[MDB_SYM_NAMLEN];
   3123 	int no_debug;
   3124 	int i;
   3125 	int depth;
   3126 	uintptr_t laddr, haddr;
   3127 
   3128 	uintptr_t caller = NULL, thread = NULL;
   3129 	uintptr_t minsize = 0, maxsize = 0;
   3130 
   3131 	hrtime_t earliest = 0, latest = 0;
   3132 
   3133 	uint_t size = 0;
   3134 	uint_t verbose = 0;
   3135 
   3136 	if (!(flags & DCMD_ADDRSPEC))
   3137 		return (DCMD_USAGE);
   3138 
   3139 	if (mdb_getopts(argc, argv,
   3140 	    'c', MDB_OPT_UINTPTR, &caller,
   3141 	    'e', MDB_OPT_UINT64, &earliest,
   3142 	    'l', MDB_OPT_UINT64, &latest,
   3143 	    's', MDB_OPT_SETBITS, TRUE, &size,
   3144 	    'm', MDB_OPT_UINTPTR, &minsize,
   3145 	    'M', MDB_OPT_UINTPTR, &maxsize,
   3146 	    't', MDB_OPT_UINTPTR, &thread,
   3147 	    'T', MDB_OPT_STR, &type,
   3148 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
   3149 	    NULL) != argc)
   3150 		return (DCMD_USAGE);
   3151 
   3152 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
   3153 		if (verbose) {
   3154 			mdb_printf("%16s %4s %16s %16s %16s\n"
   3155 			    "%<u>%16s %4s %16s %16s %16s%</u>\n",
   3156 			    "ADDR", "TYPE", "START", "END", "SIZE",
   3157 			    "", "", "THREAD", "TIMESTAMP", "");
   3158 		} else {
   3159 			mdb_printf("%?s %4s %?s %?s %s\n", "ADDR", "TYPE",
   3160 			    "START", size? "SIZE" : "END", "WHO");
   3161 		}
   3162 	}
   3163 
   3164 	if (mdb_vread(&vs, sizeof (vs), addr) == -1) {
   3165 		mdb_warn("couldn't read vmem_seg at %p", addr);
   3166 		return (DCMD_ERR);
   3167 	}
   3168 
   3169 	if (type != NULL) {
   3170 		if (strcmp(type, "ALLC") == 0 || strcmp(type, "ALLOC") == 0)
   3171 			t = VMEM_ALLOC;
   3172 		else if (strcmp(type, "FREE") == 0)
   3173 			t = VMEM_FREE;
   3174 		else if (strcmp(type, "SPAN") == 0)
   3175 			t = VMEM_SPAN;
   3176 		else if (strcmp(type, "ROTR") == 0 ||
   3177 		    strcmp(type, "ROTOR") == 0)
   3178 			t = VMEM_ROTOR;
   3179 		else if (strcmp(type, "WLKR") == 0 ||
   3180 		    strcmp(type, "WALKER") == 0)
   3181 			t = VMEM_WALKER;
   3182 		else {
   3183 			mdb_warn("\"%s\" is not a recognized vmem_seg type\n",
   3184 			    type);
   3185 			return (DCMD_ERR);
   3186 		}
   3187 
   3188 		if (vs.vs_type != t)
   3189 			return (DCMD_OK);
   3190 	}
   3191 
   3192 	sz = vs.vs_end - vs.vs_start;
   3193 
   3194 	if (minsize != 0 && sz < minsize)
   3195 		return (DCMD_OK);
   3196 
   3197 	if (maxsize != 0 && sz > maxsize)
   3198 		return (DCMD_OK);
   3199 
   3200 	t = vs.vs_type;
   3201 	depth = vs.vs_depth;
   3202 
   3203 	/*
   3204 	 * debug info, when present, is only accurate for VMEM_ALLOC segments
   3205 	 */
   3206 	no_debug = (t != VMEM_ALLOC) ||
   3207 	    (depth == 0 || depth > VMEM_STACK_DEPTH);
   3208 
   3209 	if (no_debug) {
   3210 		if (caller != NULL || thread != NULL || earliest != 0 ||
   3211 		    latest != 0)
   3212 			return (DCMD_OK);		/* not enough info */
   3213 	} else {
   3214 		if (caller != NULL) {
   3215 			laddr = caller;
   3216 			haddr = caller + sizeof (caller);
   3217 
   3218 			if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c,
   3219 			    sizeof (c), &sym) != -1 &&
   3220 			    caller == (uintptr_t)sym.st_value) {
   3221 				/*
   3222 				 * We were provided an exact symbol value; any
   3223 				 * address in the function is valid.
   3224 				 */
   3225 				laddr = (uintptr_t)sym.st_value;
   3226 				haddr = (uintptr_t)sym.st_value + sym.st_size;
   3227 			}
   3228 
   3229 			for (i = 0; i < depth; i++)
   3230 				if (vs.vs_stack[i] >= laddr &&
   3231 				    vs.vs_stack[i] < haddr)
   3232 					break;
   3233 
   3234 			if (i == depth)
   3235 				return (DCMD_OK);
   3236 		}
   3237 
   3238 		if (thread != NULL && (uintptr_t)vs.vs_thread != thread)
   3239 			return (DCMD_OK);
   3240 
   3241 		if (earliest != 0 && vs.vs_timestamp < earliest)
   3242 			return (DCMD_OK);
   3243 
   3244 		if (latest != 0 && vs.vs_timestamp > latest)
   3245 			return (DCMD_OK);
   3246 	}
   3247 
   3248 	type = (t == VMEM_ALLOC ? "ALLC" :
   3249 	    t == VMEM_FREE ? "FREE" :
   3250 	    t == VMEM_SPAN ? "SPAN" :
   3251 	    t == VMEM_ROTOR ? "ROTR" :
   3252 	    t == VMEM_WALKER ? "WLKR" :
   3253 	    "????");
   3254 
   3255 	if (flags & DCMD_PIPE_OUT) {
   3256 		mdb_printf("%#r\n", addr);
   3257 		return (DCMD_OK);
   3258 	}
   3259 
   3260 	if (verbose) {
   3261 		mdb_printf("%<b>%16p%</b> %4s %16p %16p %16d\n",
   3262 		    addr, type, vs.vs_start, vs.vs_end, sz);
   3263 
   3264 		if (no_debug)
   3265 			return (DCMD_OK);
   3266 
   3267 		mdb_printf("%16s %4s %16d %16llx\n",
   3268 		    "", "", vs.vs_thread, vs.vs_timestamp);
   3269 
   3270 		mdb_inc_indent(17);
   3271 		for (i = 0; i < depth; i++) {
   3272 			mdb_printf("%a\n", stk[i]);
   3273 		}
   3274 		mdb_dec_indent(17);
   3275 		mdb_printf("\n");
   3276 	} else {
   3277 		mdb_printf("%0?p %4s %0?p %0?p", addr, type,
   3278 		    vs.vs_start, size? sz : vs.vs_end);
   3279 
   3280 		if (no_debug) {
   3281 			mdb_printf("\n");
   3282 			return (DCMD_OK);
   3283 		}
   3284 
   3285 		for (i = 0; i < depth; i++) {
   3286 			if (mdb_lookup_by_addr(stk[i], MDB_SYM_FUZZY,
   3287 			    c, sizeof (c), &sym) == -1)
   3288 				continue;
   3289 			if (is_umem_sym(c, "vmem_"))
   3290 				continue;
   3291 			break;
   3292 		}
   3293 		mdb_printf(" %a\n", stk[i]);
   3294 	}
   3295 	return (DCMD_OK);
   3296 }
   3297 
   3298 /*ARGSUSED*/
   3299 static int
   3300 showbc(uintptr_t addr, const umem_bufctl_audit_t *bcp, hrtime_t *newest)
   3301 {
   3302 	char name[UMEM_CACHE_NAMELEN + 1];
   3303 	hrtime_t delta;
   3304 	int i, depth;
   3305 
   3306 	if (bcp->bc_timestamp == 0)
   3307 		return (WALK_DONE);
   3308 
   3309 	if (*newest == 0)
   3310 		*newest = bcp->bc_timestamp;
   3311 
   3312 	delta = *newest - bcp->bc_timestamp;
   3313 	depth = MIN(bcp->bc_depth, umem_stack_depth);
   3314 
   3315 	if (mdb_readstr(name, sizeof (name), (uintptr_t)
   3316 	    &bcp->bc_cache->cache_name) <= 0)
   3317 		(void) mdb_snprintf(name, sizeof (name), "%a", bcp->bc_cache);
   3318 
   3319 	mdb_printf("\nT-%lld.%09lld  addr=%p  %s\n",
   3320 	    delta / NANOSEC, delta % NANOSEC, bcp->bc_addr, name);
   3321 
   3322 	for (i = 0; i < depth; i++)
   3323 		mdb_printf("\t %a\n", bcp->bc_stack[i]);
   3324 
   3325 	return (WALK_NEXT);
   3326 }
   3327 
   3328 int
   3329 umalog(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   3330 {
   3331 	const char *logname = "umem_transaction_log";
   3332 	hrtime_t newest = 0;
   3333 
   3334 	if ((flags & DCMD_ADDRSPEC) || argc > 1)
   3335 		return (DCMD_USAGE);
   3336 
   3337 	if (argc > 0) {
   3338 		if (argv->a_type != MDB_TYPE_STRING)
   3339 			return (DCMD_USAGE);
   3340 		if (strcmp(argv->a_un.a_str, "fail") == 0)
   3341 			logname = "umem_failure_log";
   3342 		else if (strcmp(argv->a_un.a_str, "slab") == 0)
   3343 			logname = "umem_slab_log";
   3344 		else
   3345 			return (DCMD_USAGE);
   3346 	}
   3347 
   3348 	if (umem_readvar(&addr, logname) == -1) {
   3349 		mdb_warn("failed to read %s log header pointer");
   3350 		return (DCMD_ERR);
   3351 	}
   3352 
   3353 	if (mdb_pwalk("umem_log", (mdb_walk_cb_t)showbc, &newest, addr) == -1) {
   3354 		mdb_warn("failed to walk umem log");
   3355 		return (DCMD_ERR);
   3356 	}
   3357 
   3358 	return (DCMD_OK);
   3359 }
   3360 
   3361 /*
   3362  * As the final lure for die-hard crash(1M) users, we provide ::umausers here.
   3363  * The first piece is a structure which we use to accumulate umem_cache_t
   3364  * addresses of interest.  The umc_add is used as a callback for the umem_cache
   3365  * walker; we either add all caches, or ones named explicitly as arguments.
   3366  */
   3367 
   3368 typedef struct umclist {
   3369 	const char *umc_name;			/* Name to match (or NULL) */
   3370 	uintptr_t *umc_caches;			/* List of umem_cache_t addrs */
   3371 	int umc_nelems;				/* Num entries in umc_caches */
   3372 	int umc_size;				/* Size of umc_caches array */
   3373 } umclist_t;
   3374 
   3375 static int
   3376 umc_add(uintptr_t addr, const umem_cache_t *cp, umclist_t *umc)
   3377 {
   3378 	void *p;
   3379 	int s;
   3380 
   3381 	if (umc->umc_name == NULL ||
   3382 	    strcmp(cp->cache_name, umc->umc_name) == 0) {
   3383 		/*
   3384 		 * If we have a match, grow our array (if necessary), and then
   3385 		 * add the virtual address of the matching cache to our list.
   3386 		 */
   3387 		if (umc->umc_nelems >= umc->umc_size) {
   3388 			s = umc->umc_size ? umc->umc_size * 2 : 256;
   3389 			p = mdb_alloc(sizeof (uintptr_t) * s, UM_SLEEP | UM_GC);
   3390 
   3391 			bcopy(umc->umc_caches, p,
   3392 			    sizeof (uintptr_t) * umc->umc_size);
   3393 
   3394 			umc->umc_caches = p;
   3395 			umc->umc_size = s;
   3396 		}
   3397 
   3398 		umc->umc_caches[umc->umc_nelems++] = addr;
   3399 		return (umc->umc_name ? WALK_DONE : WALK_NEXT);
   3400 	}
   3401 
   3402 	return (WALK_NEXT);
   3403 }
   3404 
   3405 /*
   3406  * The second piece of ::umausers is a hash table of allocations.  Each
   3407  * allocation owner is identified by its stack trace and data_size.  We then
   3408  * track the total bytes of all such allocations, and the number of allocations
   3409  * to report at the end.  Once we have a list of caches, we walk through the
   3410  * allocated bufctls of each, and update our hash table accordingly.
   3411  */
   3412 
   3413 typedef struct umowner {
   3414 	struct umowner *umo_head;		/* First hash elt in bucket */
   3415 	struct umowner *umo_next;		/* Next hash elt in chain */
   3416 	size_t umo_signature;			/* Hash table signature */
   3417 	uint_t umo_num;				/* Number of allocations */
   3418 	size_t umo_data_size;			/* Size of each allocation */
   3419 	size_t umo_total_size;			/* Total bytes of allocation */
   3420 	int umo_depth;				/* Depth of stack trace */
   3421 	uintptr_t *umo_stack;			/* Stack trace */
   3422 } umowner_t;
   3423 
   3424 typedef struct umusers {
   3425 	const umem_cache_t *umu_cache;		/* Current umem cache */
   3426 	umowner_t *umu_hash;			/* Hash table of owners */
   3427 	uintptr_t *umu_stacks;			/* stacks for owners */
   3428 	int umu_nelems;				/* Number of entries in use */
   3429 	int umu_size;				/* Total number of entries */
   3430 } umusers_t;
   3431 
   3432 static void
   3433 umu_add(umusers_t *umu, const umem_bufctl_audit_t *bcp,
   3434     size_t size, size_t data_size)
   3435 {
   3436 	int i, depth = MIN(bcp->bc_depth, umem_stack_depth);
   3437 	size_t bucket, signature = data_size;
   3438 	umowner_t *umo, *umoend;
   3439 
   3440 	/*
   3441 	 * If the hash table is full, double its size and rehash everything.
   3442 	 */
   3443 	if (umu->umu_nelems >= umu->umu_size) {
   3444 		int s = umu->umu_size ? umu->umu_size * 2 : 1024;
   3445 		size_t umowner_size = sizeof (umowner_t);
   3446 		size_t trace_size = umem_stack_depth * sizeof (uintptr_t);
   3447 		uintptr_t *new_stacks;
   3448 
   3449 		umo = mdb_alloc(umowner_size * s, UM_SLEEP | UM_GC);
   3450 		new_stacks = mdb_alloc(trace_size * s, UM_SLEEP | UM_GC);
   3451 
   3452 		bcopy(umu->umu_hash, umo, umowner_size * umu->umu_size);
   3453 		bcopy(umu->umu_stacks, new_stacks, trace_size * umu->umu_size);
   3454 		umu->umu_hash = umo;
   3455 		umu->umu_stacks = new_stacks;
   3456 		umu->umu_size = s;
   3457 
   3458 		umoend = umu->umu_hash + umu->umu_size;
   3459 		for (umo = umu->umu_hash; umo < umoend; umo++) {
   3460 			umo->umo_head = NULL;
   3461 			umo->umo_stack = &umu->umu_stacks[
   3462 			    umem_stack_depth * (umo - umu->umu_hash)];
   3463 		}
   3464 
   3465 		umoend = umu->umu_hash + umu->umu_nelems;
   3466 		for (umo = umu->umu_hash; umo < umoend; umo++) {
   3467 			bucket = umo->umo_signature & (umu->umu_size - 1);
   3468 			umo->umo_next = umu->umu_hash[bucket].umo_head;
   3469 			umu->umu_hash[bucket].umo_head = umo;
   3470 		}
   3471 	}
   3472 
   3473 	/*
   3474 	 * Finish computing the hash signature from the stack trace, and then
   3475 	 * see if the owner is in the hash table.  If so, update our stats.
   3476 	 */
   3477 	for (i = 0; i < depth; i++)
   3478 		signature += bcp->bc_stack[i];
   3479 
   3480 	bucket = signature & (umu->umu_size - 1);
   3481 
   3482 	for (umo = umu->umu_hash[bucket].umo_head; umo; umo = umo->umo_next) {
   3483 		if (umo->umo_signature == signature) {
   3484 			size_t difference = 0;
   3485 
   3486 			difference |= umo->umo_data_size - data_size;
   3487 			difference |= umo->umo_depth - depth;
   3488 
   3489 			for (i = 0; i < depth; i++) {
   3490 				difference |= umo->umo_stack[i] -
   3491 				    bcp->bc_stack[i];
   3492 			}
   3493 
   3494 			if (difference == 0) {
   3495 				umo->umo_total_size += size;
   3496 				umo->umo_num++;
   3497 				return;
   3498 			}
   3499 		}
   3500 	}
   3501 
   3502 	/*
   3503 	 * If the owner is not yet hashed, grab the next element and fill it
   3504 	 * in based on the allocation information.
   3505 	 */
   3506 	umo = &umu->umu_hash[umu->umu_nelems++];
   3507 	umo->umo_next = umu->umu_hash[bucket].umo_head;
   3508 	umu->umu_hash[bucket].umo_head = umo;
   3509 
   3510 	umo->umo_signature = signature;
   3511 	umo->umo_num = 1;
   3512 	umo->umo_data_size = data_size;
   3513 	umo->umo_total_size = size;
   3514 	umo->umo_depth = depth;
   3515 
   3516 	for (i = 0; i < depth; i++)
   3517 		umo->umo_stack[i] = bcp->bc_stack[i];
   3518 }
   3519 
   3520 /*
   3521  * When ::umausers is invoked without the -f flag, we simply update our hash
   3522  * table with the information from each allocated bufctl.
   3523  */
   3524 /*ARGSUSED*/
   3525 static int
   3526 umause1(uintptr_t addr, const umem_bufctl_audit_t *bcp, umusers_t *umu)
   3527 {
   3528 	const umem_cache_t *cp = umu->umu_cache;
   3529 
   3530 	umu_add(umu, bcp, cp->cache_bufsize, cp->cache_bufsize);
   3531 	return (WALK_NEXT);
   3532 }
   3533 
   3534 /*
   3535  * When ::umausers is invoked with the -f flag, we print out the information
   3536  * for each bufctl as well as updating the hash table.
   3537  */
   3538 static int
   3539 umause2(uintptr_t addr, const umem_bufctl_audit_t *bcp, umusers_t *umu)
   3540 {
   3541 	int i, depth = MIN(bcp->bc_depth, umem_stack_depth);
   3542 	const umem_cache_t *cp = umu->umu_cache;
   3543 
   3544 	mdb_printf("size %d, addr %p, thread %p, cache %s\n",
   3545 	    cp->cache_bufsize, addr, bcp->bc_thread, cp->cache_name);
   3546 
   3547 	for (i = 0; i < depth; i++)
   3548 		mdb_printf("\t %a\n", bcp->bc_stack[i]);
   3549 
   3550 	umu_add(umu, bcp, cp->cache_bufsize, cp->cache_bufsize);
   3551 	return (WALK_NEXT);
   3552 }
   3553 
   3554 /*
   3555  * We sort our results by allocation size before printing them.
   3556  */
   3557 static int
   3558 umownercmp(const void *lp, const void *rp)
   3559 {
   3560 	const umowner_t *lhs = lp;
   3561 	const umowner_t *rhs = rp;
   3562 
   3563 	return (rhs->umo_total_size - lhs->umo_total_size);
   3564 }
   3565 
   3566 /*
   3567  * The main engine of ::umausers is relatively straightforward: First we
   3568  * accumulate our list of umem_cache_t addresses into the umclist_t. Next we
   3569  * iterate over the allocated bufctls of each cache in the list.  Finally,
   3570  * we sort and print our results.
   3571  */
   3572 /*ARGSUSED*/
   3573 int
   3574 umausers(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   3575 {
   3576 	int mem_threshold = 8192;	/* Minimum # bytes for printing */
   3577 	int cnt_threshold = 100;	/* Minimum # blocks for printing */
   3578 	int audited_caches = 0;		/* Number of UMF_AUDIT caches found */
   3579 	int do_all_caches = 1;		/* Do all caches (no arguments) */
   3580 	int opt_e = FALSE;		/* Include "small" users */
   3581 	int opt_f = FALSE;		/* Print stack traces */
   3582 
   3583 	mdb_walk_cb_t callback = (mdb_walk_cb_t)umause1;
   3584 	umowner_t *umo, *umoend;
   3585 	int i, oelems;
   3586 
   3587 	umclist_t umc;
   3588 	umusers_t umu;
   3589 
   3590 	if (flags & DCMD_ADDRSPEC)
   3591 		return (DCMD_USAGE);
   3592 
   3593 	bzero(&umc, sizeof (umc));
   3594 	bzero(&umu, sizeof (umu));
   3595 
   3596 	while ((i = mdb_getopts(argc, argv,
   3597 	    'e', MDB_OPT_SETBITS, TRUE, &opt_e,
   3598 	    'f', MDB_OPT_SETBITS, TRUE, &opt_f, NULL)) != argc) {
   3599 
   3600 		argv += i;	/* skip past options we just processed */
   3601 		argc -= i;	/* adjust argc */
   3602 
   3603 		if (argv->a_type != MDB_TYPE_STRING || *argv->a_un.a_str == '-')
   3604 			return (DCMD_USAGE);
   3605 
   3606 		oelems = umc.umc_nelems;
   3607 		umc.umc_name = argv->a_un.a_str;
   3608 		(void) mdb_walk("umem_cache", (mdb_walk_cb_t)umc_add, &umc);
   3609 
   3610 		if (umc.umc_nelems == oelems) {
   3611 			mdb_warn("unknown umem cache: %s\n", umc.umc_name);
   3612 			return (DCMD_ERR);
   3613 		}
   3614 
   3615 		do_all_caches = 0;
   3616 		argv++;
   3617 		argc--;
   3618 	}
   3619 
   3620 	if (opt_e)
   3621 		mem_threshold = cnt_threshold = 0;
   3622 
   3623 	if (opt_f)
   3624 		callback = (mdb_walk_cb_t)umause2;
   3625 
   3626 	if (do_all_caches) {
   3627 		umc.umc_name = NULL; /* match all cache names */
   3628 		(void) mdb_walk("umem_cache", (mdb_walk_cb_t)umc_add, &umc);
   3629 	}
   3630 
   3631 	for (i = 0; i < umc.umc_nelems; i++) {
   3632 		uintptr_t cp = umc.umc_caches[i];
   3633 		umem_cache_t c;
   3634 
   3635 		if (mdb_vread(&c, sizeof (c), cp) == -1) {
   3636 			mdb_warn("failed to read cache at %p", cp);
   3637 			continue;
   3638 		}
   3639 
   3640 		if (!(c.cache_flags & UMF_AUDIT)) {
   3641 			if (!do_all_caches) {
   3642 				mdb_warn("UMF_AUDIT is not enabled for %s\n",
   3643 				    c.cache_name);
   3644 			}
   3645 			continue;
   3646 		}
   3647 
   3648 		umu.umu_cache = &c;
   3649 		(void) mdb_pwalk("bufctl", callback, &umu, cp);
   3650 		audited_caches++;
   3651 	}
   3652 
   3653 	if (audited_caches == 0 && do_all_caches) {
   3654 		mdb_warn("UMF_AUDIT is not enabled for any caches\n");
   3655 		return (DCMD_ERR);
   3656 	}
   3657 
   3658 	qsort(umu.umu_hash, umu.umu_nelems, sizeof (umowner_t), umownercmp);
   3659 	umoend = umu.umu_hash + umu.umu_nelems;
   3660 
   3661 	for (umo = umu.umu_hash; umo < umoend; umo++) {
   3662 		if (umo->umo_total_size < mem_threshold &&
   3663 		    umo->umo_num < cnt_threshold)
   3664 			continue;
   3665 		mdb_printf("%lu bytes for %u allocations with data size %lu:\n",
   3666 		    umo->umo_total_size, umo->umo_num, umo->umo_data_size);
   3667 		for (i = 0; i < umo->umo_depth; i++)
   3668 			mdb_printf("\t %a\n", umo->umo_stack[i]);
   3669 	}
   3670 
   3671 	return (DCMD_OK);
   3672 }
   3673 
   3674 struct malloc_data {
   3675 	uint32_t malloc_size;
   3676 	uint32_t malloc_stat; /* == UMEM_MALLOC_ENCODE(state, malloc_size) */
   3677 };
   3678 
   3679 #ifdef _LP64
   3680 #define	UMI_MAX_BUCKET		(UMEM_MAXBUF - 2*sizeof (struct malloc_data))
   3681 #else
   3682 #define	UMI_MAX_BUCKET		(UMEM_MAXBUF - sizeof (struct malloc_data))
   3683 #endif
   3684 
   3685 typedef struct umem_malloc_info {
   3686 	size_t um_total;	/* total allocated buffers */
   3687 	size_t um_malloc;	/* malloc buffers */
   3688 	size_t um_malloc_size;	/* sum of malloc buffer sizes */
   3689 	size_t um_malloc_overhead; /* sum of in-chunk overheads */
   3690 
   3691 	umem_cache_t *um_cp;
   3692 
   3693 	uint_t *um_bucket;
   3694 } umem_malloc_info_t;
   3695 
   3696 static void
   3697 umem_malloc_print_dist(uint_t *um_bucket, size_t minmalloc, size_t maxmalloc,
   3698     size_t maxbuckets, size_t minbucketsize, int geometric)
   3699 {
   3700 	uint64_t um_malloc;
   3701 	int minb = -1;
   3702 	int maxb = -1;
   3703 	int buckets;
   3704 	int nbucks;
   3705 	int i;
   3706 	int b;
   3707 	const int *distarray;
   3708 
   3709 	minb = (int)minmalloc;
   3710 	maxb = (int)maxmalloc;
   3711 
   3712 	nbucks = buckets = maxb - minb + 1;
   3713 
   3714 	um_malloc = 0;
   3715 	for (b = minb; b <= maxb; b++)
   3716 		um_malloc += um_bucket[b];
   3717 
   3718 	if (maxbuckets != 0)
   3719 		buckets = MIN(buckets, maxbuckets);
   3720 
   3721 	if (minbucketsize > 1) {
   3722 		buckets = MIN(buckets, nbucks/minbucketsize);
   3723 		if (buckets == 0) {
   3724 			buckets = 1;
   3725 			minbucketsize = nbucks;
   3726 		}
   3727 	}
   3728 
   3729 	if (geometric)
   3730 		distarray = dist_geometric(buckets, minb, maxb, minbucketsize);
   3731 	else
   3732 		distarray = dist_linear(buckets, minb, maxb);
   3733 
   3734 	dist_print_header("malloc size", 11, "count");
   3735 	for (i = 0; i < buckets; i++) {
   3736 		dist_print_bucket(distarray, i, um_bucket, um_malloc, 11);
   3737 	}
   3738 	mdb_printf("\n");
   3739 }
   3740 
   3741 /*
   3742  * A malloc()ed buffer looks like:
   3743  *
   3744  *	<----------- mi.malloc_size --->
   3745  *	<----------- cp.cache_bufsize ------------------>
   3746  *	<----------- cp.cache_chunksize -------------------------------->
   3747  *	+-------+-----------------------+---------------+---------------+
   3748  *	|/tag///| mallocsz		|/round-off/////|/debug info////|
   3749  *	+-------+---------------------------------------+---------------+
   3750  *		<-- usable space ------>
   3751  *
   3752  * mallocsz is the argument to malloc(3C).
   3753  * mi.malloc_size is the actual size passed to umem_alloc(), which
   3754  * is rounded up to the smallest available cache size, which is
   3755  * cache_bufsize.  If there is debugging or alignment overhead in
   3756  * the cache, that is reflected in a larger cache_chunksize.
   3757  *
   3758  * The tag at the beginning of the buffer is either 8-bytes or 16-bytes,
   3759  * depending upon the ISA's alignment requirements.  For 32-bit allocations,
   3760  * it is always a 8-byte tag.  For 64-bit allocations larger than 8 bytes,
   3761  * the tag has 8 bytes of padding before it.
   3762  *
   3763  * 32-byte, 64-byte buffers <= 8 bytes:
   3764  *	+-------+-------+--------- ...
   3765  *	|/size//|/stat//| mallocsz ...
   3766  *	+-------+-------+--------- ...
   3767  *			^
   3768  *			pointer returned from malloc(3C)
   3769  *
   3770  * 64-byte buffers > 8 bytes:
   3771  *	+---------------+-------+-------+--------- ...
   3772  *	|/padding///////|/size//|/stat//| mallocsz ...
   3773  *	+---------------+-------+-------+--------- ...
   3774  *					^
   3775  *					pointer returned from malloc(3C)
   3776  *
   3777  * The "size" field is "malloc_size", which is mallocsz + the padding.
   3778  * The "stat" field is derived from malloc_size, and functions as a
   3779  * validation that this buffer is actually from malloc(3C).
   3780  */
   3781 /*ARGSUSED*/
   3782 static int
   3783 um_umem_buffer_cb(uintptr_t addr, void *buf, umem_malloc_info_t *ump)
   3784 {
   3785 	struct malloc_data md;
   3786 	size_t m_addr = addr;
   3787 	size_t overhead = sizeof (md);
   3788 	size_t mallocsz;
   3789 
   3790 	ump->um_total++;
   3791 
   3792 #ifdef _LP64
   3793 	if (ump->um_cp->cache_bufsize > UMEM_SECOND_ALIGN) {
   3794 		m_addr += overhead;
   3795 		overhead += sizeof (md);
   3796 	}
   3797 #endif
   3798 
   3799 	if (mdb_vread(&md, sizeof (md), m_addr) == -1) {
   3800 		mdb_warn("unable to read malloc header at %p", m_addr);
   3801 		return (WALK_NEXT);
   3802 	}
   3803 
   3804 	switch (UMEM_MALLOC_DECODE(md.malloc_stat, md.malloc_size)) {
   3805 	case MALLOC_MAGIC:
   3806 #ifdef _LP64
   3807 	case MALLOC_SECOND_MAGIC:
   3808 #endif
   3809 		mallocsz = md.malloc_size - overhead;
   3810 
   3811 		ump->um_malloc++;
   3812 		ump->um_malloc_size += mallocsz;
   3813 		ump->um_malloc_overhead += overhead;
   3814 
   3815 		/* include round-off and debug overhead */
   3816 		ump->um_malloc_overhead +=
   3817 		    ump->um_cp->cache_chunksize - md.malloc_size;
   3818 
   3819 		if (ump->um_bucket != NULL && mallocsz <= UMI_MAX_BUCKET)
   3820 			ump->um_bucket[mallocsz]++;
   3821 
   3822 		break;
   3823 	default:
   3824 		break;
   3825 	}
   3826 
   3827 	return (WALK_NEXT);
   3828 }
   3829 
   3830 int
   3831 get_umem_alloc_sizes(int **out, size_t *out_num)
   3832 {
   3833 	GElf_Sym sym;
   3834 
   3835 	if (umem_lookup_by_name("umem_alloc_sizes", &sym) == -1) {
   3836 		mdb_warn("unable to look up umem_alloc_sizes");
   3837 		return (-1);
   3838 	}
   3839 
   3840 	*out = mdb_alloc(sym.st_size, UM_SLEEP | UM_GC);
   3841 	*out_num = sym.st_size / sizeof (int);
   3842 
   3843 	if (mdb_vread(*out, sym.st_size, sym.st_value) == -1) {
   3844 		mdb_warn("unable to read umem_alloc_sizes (%p)", sym.st_value);
   3845 		*out = NULL;
   3846 		return (-1);
   3847 	}
   3848 
   3849 	return (0);
   3850 }
   3851 
   3852 
   3853 static int
   3854 um_umem_cache_cb(uintptr_t addr, umem_cache_t *cp, umem_malloc_info_t *ump)
   3855 {
   3856 	if (strncmp(cp->cache_name, "umem_alloc_", strlen("umem_alloc_")) != 0)
   3857 		return (WALK_NEXT);
   3858 
   3859 	ump->um_cp = cp;
   3860 
   3861 	if (mdb_pwalk("umem", (mdb_walk_cb_t)um_umem_buffer_cb, ump, addr) ==
   3862 	    -1) {
   3863 		mdb_warn("can't walk 'umem' for cache %p", addr);
   3864 		return (WALK_ERR);
   3865 	}
   3866 
   3867 	return (WALK_NEXT);
   3868 }
   3869 
   3870 void
   3871 umem_malloc_dist_help(void)
   3872 {
   3873 	mdb_printf("%s\n",
   3874 	    "report distribution of outstanding malloc()s");
   3875 	mdb_dec_indent(2);
   3876 	mdb_printf("%<b>OPTIONS%</b>\n");
   3877 	mdb_inc_indent(2);
   3878 	mdb_printf("%s",
   3879 "  -b maxbins\n"
   3880 "        Use at most maxbins bins for the data\n"
   3881 "  -B minbinsize\n"
   3882 "        Make the bins at least minbinsize bytes apart\n"
   3883 "  -d    dump the raw data out, without binning\n"
   3884 "  -g    use geometric binning instead of linear binning\n");
   3885 }
   3886 
   3887 /*ARGSUSED*/
   3888 int
   3889 umem_malloc_dist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   3890 {
   3891 	umem_malloc_info_t mi;
   3892 	uint_t geometric = 0;
   3893 	uint_t dump = 0;
   3894 	size_t maxbuckets = 0;
   3895 	size_t minbucketsize = 0;
   3896 
   3897 	size_t minalloc = 0;
   3898 	size_t maxalloc = UMI_MAX_BUCKET;
   3899 
   3900 	if (flags & DCMD_ADDRSPEC)
   3901 		return (DCMD_USAGE);
   3902 
   3903 	if (mdb_getopts(argc, argv,
   3904 	    'd', MDB_OPT_SETBITS, TRUE, &dump,
   3905 	    'g', MDB_OPT_SETBITS, TRUE, &geometric,
   3906 	    'b', MDB_OPT_UINTPTR, &maxbuckets,
   3907 	    'B', MDB_OPT_UINTPTR, &minbucketsize,
   3908 	    0) != argc)
   3909 		return (DCMD_USAGE);
   3910 
   3911 	bzero(&mi, sizeof (mi));
   3912 	mi.um_bucket = mdb_zalloc((UMI_MAX_BUCKET + 1) * sizeof (*mi.um_bucket),
   3913 	    UM_SLEEP | UM_GC);
   3914 
   3915 	if (mdb_walk("umem_cache", (mdb_walk_cb_t)um_umem_cache_cb,
   3916 	    &mi) == -1) {
   3917 		mdb_warn("unable to walk 'umem_cache'");
   3918 		return (DCMD_ERR);
   3919 	}
   3920 
   3921 	if (dump) {
   3922 		int i;
   3923 		for (i = minalloc; i <= maxalloc; i++)
   3924 			mdb_printf("%d\t%d\n", i, mi.um_bucket[i]);
   3925 
   3926 		return (DCMD_OK);
   3927 	}
   3928 
   3929 	umem_malloc_print_dist(mi.um_bucket, minalloc, maxalloc,
   3930 	    maxbuckets, minbucketsize, geometric);
   3931 
   3932 	return (DCMD_OK);
   3933 }
   3934 
   3935 void
   3936 umem_malloc_info_help(void)
   3937 {
   3938 	mdb_printf("%s\n",
   3939 	    "report information about malloc()s by cache.  ");
   3940 	mdb_dec_indent(2);
   3941 	mdb_printf("%<b>OPTIONS%</b>\n");
   3942 	mdb_inc_indent(2);
   3943 	mdb_printf("%s",
   3944 "  -b maxbins\n"
   3945 "        Use at most maxbins bins for the data\n"
   3946 "  -B minbinsize\n"
   3947 "        Make the bins at least minbinsize bytes apart\n"
   3948 "  -d    dump the raw distribution data without binning\n"
   3949 #ifndef _KMDB
   3950 "  -g    use geometric binning instead of linear binning\n"
   3951 #endif
   3952 	    "");
   3953 }
   3954 int
   3955 umem_malloc_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   3956 {
   3957 	umem_cache_t c;
   3958 	umem_malloc_info_t mi;
   3959 
   3960 	int skip = 0;
   3961 
   3962 	size_t maxmalloc;
   3963 	size_t overhead;
   3964 	size_t allocated;
   3965 	size_t avg_malloc;
   3966 	size_t overhead_pct;	/* 1000 * overhead_percent */
   3967 
   3968 	uint_t verbose = 0;
   3969 	uint_t dump = 0;
   3970 	uint_t geometric = 0;
   3971 	size_t maxbuckets = 0;
   3972 	size_t minbucketsize = 0;
   3973 
   3974 	int *alloc_sizes;
   3975 	int idx;
   3976 	size_t num;
   3977 	size_t minmalloc;
   3978 
   3979 	if (mdb_getopts(argc, argv,
   3980 	    'd', MDB_OPT_SETBITS, TRUE, &dump,
   3981 	    'g', MDB_OPT_SETBITS, TRUE, &geometric,
   3982 	    'b', MDB_OPT_UINTPTR, &maxbuckets,
   3983 	    'B', MDB_OPT_UINTPTR, &minbucketsize,
   3984 	    0) != argc)
   3985 		return (DCMD_USAGE);
   3986 
   3987 	if (dump || geometric || (maxbuckets != 0) || (minbucketsize != 0))
   3988 		verbose = 1;
   3989 
   3990 	if (!(flags & DCMD_ADDRSPEC)) {
   3991 		if (mdb_walk_dcmd("umem_cache", "umem_malloc_info",
   3992 		    argc, argv) == -1) {
   3993 			mdb_warn("can't walk umem_cache");
   3994 			return (DCMD_ERR);
   3995 		}
   3996 		return (DCMD_OK);
   3997 	}
   3998 
   3999 	if (!mdb_vread(&c, sizeof (c), addr)) {
   4000 		mdb_warn("unable to read cache at %p", addr);
   4001 		return (DCMD_ERR);
   4002 	}
   4003 
   4004 	if (strncmp(c.cache_name, "umem_alloc_", strlen("umem_alloc_")) != 0) {
   4005 		if (!(flags & DCMD_LOOP))
   4006 			mdb_warn("umem_malloc_info: cache \"%s\" is not used "
   4007 			    "by malloc()\n", c.cache_name);
   4008 		skip = 1;
   4009 	}
   4010 
   4011 	/*
   4012 	 * normally, print the header only the first time.  In verbose mode,
   4013 	 * print the header on every non-skipped buffer
   4014 	 */
   4015 	if ((!verbose && DCMD_HDRSPEC(flags)) || (verbose && !skip))
   4016 		mdb_printf("%<ul>%-?s %6s %6s %8s %8s %10s %10s %6s%</ul>\n",
   4017 		    "CACHE", "BUFSZ", "MAXMAL",
   4018 		    "BUFMALLC", "AVG_MAL", "MALLOCED", "OVERHEAD", "%OVER");
   4019 
   4020 	if (skip)
   4021 		return (DCMD_OK);
   4022 
   4023 	maxmalloc = c.cache_bufsize - sizeof (struct malloc_data);
   4024 #ifdef _LP64
   4025 	if (c.cache_bufsize > UMEM_SECOND_ALIGN)
   4026 		maxmalloc -= sizeof (struct malloc_data);
   4027 #endif
   4028 
   4029 	bzero(&mi, sizeof (mi));
   4030 	mi.um_cp = &c;
   4031 	if (verbose)
   4032 		mi.um_bucket =
   4033 		    mdb_zalloc((UMI_MAX_BUCKET + 1) * sizeof (*mi.um_bucket),
   4034 		    UM_SLEEP | UM_GC);
   4035 
   4036 	if (mdb_pwalk("umem", (mdb_walk_cb_t)um_umem_buffer_cb, &mi, addr) ==
   4037 	    -1) {
   4038 		mdb_warn("can't walk 'umem'");
   4039 		return (DCMD_ERR);
   4040 	}
   4041 
   4042 	overhead = mi.um_malloc_overhead;
   4043 	allocated = mi.um_malloc_size;
   4044 
   4045 	/* do integer round off for the average */
   4046 	if (mi.um_malloc != 0)
   4047 		avg_malloc = (allocated + (mi.um_malloc - 1)/2) / mi.um_malloc;
   4048 	else
   4049 		avg_malloc = 0;
   4050 
   4051 	/*
   4052 	 * include per-slab overhead
   4053 	 *
   4054 	 * Each slab in a given cache is the same size, and has the same
   4055 	 * number of chunks in it;  we read in the first slab on the
   4056 	 * slab list to get the number of chunks for all slabs.  To
   4057 	 * compute the per-slab overhead, we just subtract the chunk usage
   4058 	 * from the slabsize:
   4059 	 *
   4060 	 * +------------+-------+-------+ ... --+-------+-------+-------+
   4061 	 * |////////////|	|	| ...	|	|///////|///////|
   4062 	 * |////color///| chunk	| chunk	| ...	| chunk	|/color/|/slab//|
   4063 	 * |////////////|	|	| ...	|	|///////|///////|
   4064 	 * +------------+-------+-------+ ... --+-------+-------+-------+
   4065 	 * |		\_______chunksize * chunks_____/		|
   4066 	 * \__________________________slabsize__________________________/
   4067 	 *
   4068 	 * For UMF_HASH caches, there is an additional source of overhead;
   4069 	 * the external umem_slab_t and per-chunk bufctl structures.  We
   4070 	 * include those in our per-slab overhead.
   4071 	 *
   4072 	 * Once we have a number for the per-slab overhead, we estimate
   4073 	 * the actual overhead by treating the malloc()ed buffers as if
   4074 	 * they were densely packed:
   4075 	 *
   4076 	 *	additional overhead = (# mallocs) * (per-slab) / (chunks);
   4077 	 *
   4078 	 * carefully ordering the multiply before the divide, to avoid
   4079 	 * round-off error.
   4080 	 */
   4081 	if (mi.um_malloc != 0) {
   4082 		umem_slab_t slab;
   4083 		uintptr_t saddr = (uintptr_t)c.cache_nullslab.slab_next;
   4084 
   4085 		if (mdb_vread(&slab, sizeof (slab), saddr) == -1) {
   4086 			mdb_warn("unable to read slab at %p\n", saddr);
   4087 		} else {
   4088 			long chunks = slab.slab_chunks;
   4089 			if (chunks != 0 && c.cache_chunksize != 0 &&
   4090 			    chunks <= c.cache_slabsize / c.cache_chunksize) {
   4091 				uintmax_t perslab =
   4092 				    c.cache_slabsize -
   4093 				    (c.cache_chunksize * chunks);
   4094 
   4095 				if (c.cache_flags & UMF_HASH) {
   4096 					perslab += sizeof (umem_slab_t) +
   4097 					    chunks *
   4098 					    ((c.cache_flags & UMF_AUDIT) ?
   4099 					    sizeof (umem_bufctl_audit_t) :
   4100 					    sizeof (umem_bufctl_t));
   4101 				}
   4102 				overhead +=
   4103 				    (perslab * (uintmax_t)mi.um_malloc)/chunks;
   4104 			} else {
   4105 				mdb_warn("invalid #chunks (%d) in slab %p\n",
   4106 				    chunks, saddr);
   4107 			}
   4108 		}
   4109 	}
   4110 
   4111 	if (allocated != 0)
   4112 		overhead_pct = (1000ULL * overhead) / allocated;
   4113 	else
   4114 		overhead_pct = 0;
   4115 
   4116 	mdb_printf("%0?p %6ld %6ld %8ld %8ld %10ld %10ld %3ld.%01ld%%\n",
   4117 	    addr, c.cache_bufsize, maxmalloc,
   4118 	    mi.um_malloc, avg_malloc, allocated, overhead,
   4119 	    overhead_pct / 10, overhead_pct % 10);
   4120 
   4121 	if (!verbose)
   4122 		return (DCMD_OK);
   4123 
   4124 	if (!dump)
   4125 		mdb_printf("\n");
   4126 
   4127 	if (get_umem_alloc_sizes(&alloc_sizes, &num) == -1)
   4128 		return (DCMD_ERR);
   4129 
   4130 	for (idx = 0; idx < num; idx++) {
   4131 		if (alloc_sizes[idx] == c.cache_bufsize)
   4132 			break;
   4133 		if (alloc_sizes[idx] == 0) {
   4134 			idx = num;	/* 0-terminated array */
   4135 			break;
   4136 		}
   4137 	}
   4138 	if (idx == num) {
   4139 		mdb_warn(
   4140 		    "cache %p's size (%d) not in umem_alloc_sizes\n",
   4141 		    addr, c.cache_bufsize);
   4142 		return (DCMD_ERR);
   4143 	}
   4144 
   4145 	minmalloc = (idx == 0)? 0 : alloc_sizes[idx - 1];
   4146 	if (minmalloc > 0) {
   4147 #ifdef _LP64
   4148 		if (minmalloc > UMEM_SECOND_ALIGN)
   4149 			minmalloc -= sizeof (struct malloc_data);
   4150 #endif
   4151 		minmalloc -= sizeof (struct malloc_data);
   4152 		minmalloc += 1;
   4153 	}
   4154 
   4155 	if (dump) {
   4156 		for (idx = minmalloc; idx <= maxmalloc; idx++)
   4157 			mdb_printf("%d\t%d\n", idx, mi.um_bucket[idx]);
   4158 		mdb_printf("\n");
   4159 	} else {
   4160 		umem_malloc_print_dist(mi.um_bucket, minmalloc, maxmalloc,
   4161 		    maxbuckets, minbucketsize, geometric);
   4162 	}
   4163 
   4164 	return (DCMD_OK);
   4165 }
   4166