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