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