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