xref: /illumos-gate/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * explicitly define DTRACE_ERRDEBUG to pull in definition of dtrace_errhash_t
31  * explicitly define _STDARG_H to avoid stdarg.h/varargs.h u/k defn conflict
32  */
33 #define	DTRACE_ERRDEBUG
34 #define	_STDARG_H
35 
36 #include <mdb/mdb_param.h>
37 #include <mdb/mdb_modapi.h>
38 #include <mdb/mdb_ks.h>
39 #include <sys/dtrace_impl.h>
40 #include <sys/vmem_impl.h>
41 #include <sys/ddi_impldefs.h>
42 #include <sys/sysmacros.h>
43 #include <sys/kobj.h>
44 #include <dtrace.h>
45 #include <alloca.h>
46 #include <ctype.h>
47 #include <errno.h>
48 #include <math.h>
49 
50 #ifdef _LP64
51 #define	DIFO_ADDRWIDTH		11
52 #else
53 #define	DIFO_ADDRWIDTH		8
54 #endif
55 
56 int dof_sec(uintptr_t, uint_t, int, const mdb_arg_t *);
57 
58 /*ARGSUSED*/
59 static void
60 dis_log(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
61 {
62 	mdb_printf("%-4s %%r%u, %%r%u, %%r%u", name,
63 	    DIF_INSTR_R1(instr), DIF_INSTR_R2(instr), DIF_INSTR_RD(instr));
64 }
65 
66 /*ARGSUSED*/
67 static void
68 dis_branch(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
69 {
70 	mdb_printf("%-4s %u", name, DIF_INSTR_LABEL(instr));
71 }
72 
73 /*ARGSUSED*/
74 static void
75 dis_load(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
76 {
77 	mdb_printf("%-4s [%%r%u], %%r%u", name,
78 	    DIF_INSTR_R1(instr), DIF_INSTR_RD(instr));
79 }
80 
81 /*ARGSUSED*/
82 static void
83 dis_store(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
84 {
85 	mdb_printf("%-4s %%r%u, [%%r%u]", name,
86 	    DIF_INSTR_R1(instr), DIF_INSTR_RD(instr));
87 }
88 
89 /*ARGSUSED*/
90 static void
91 dis_str(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
92 {
93 	mdb_printf("%s", name);
94 }
95 
96 /*ARGSUSED*/
97 static void
98 dis_r1rd(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
99 {
100 	mdb_printf("%-4s %%r%u, %%r%u", name,
101 	    DIF_INSTR_R1(instr), DIF_INSTR_RD(instr));
102 }
103 
104 /*ARGSUSED*/
105 static void
106 dis_cmp(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
107 {
108 	mdb_printf("%-4s %%r%u, %%r%u", name,
109 	    DIF_INSTR_R1(instr), DIF_INSTR_R2(instr));
110 }
111 
112 /*ARGSUSED*/
113 static void
114 dis_tst(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
115 {
116 	mdb_printf("%-4s %%r%u", name, DIF_INSTR_R1(instr));
117 }
118 
119 static const char *
120 dis_varname(const dtrace_difo_t *dp, uint_t id, uint_t scope)
121 {
122 	dtrace_difv_t *dvp;
123 	size_t varsize;
124 	caddr_t addr = NULL, str;
125 	uint_t i;
126 
127 	if (dp == NULL)
128 		return (NULL);
129 
130 	varsize = sizeof (dtrace_difv_t) * dp->dtdo_varlen;
131 	dvp = mdb_alloc(varsize, UM_SLEEP);
132 
133 	if (mdb_vread(dvp, varsize, (uintptr_t)dp->dtdo_vartab) == -1) {
134 		mdb_free(dvp, varsize);
135 		return ("<unreadable>");
136 	}
137 
138 	for (i = 0; i < dp->dtdo_varlen; i++) {
139 		if (dvp[i].dtdv_id == id && dvp[i].dtdv_scope == scope) {
140 			if (dvp[i].dtdv_name < dp->dtdo_strlen)
141 				addr = dp->dtdo_strtab + dvp[i].dtdv_name;
142 			break;
143 		}
144 	}
145 
146 	mdb_free(dvp, varsize);
147 
148 	if (addr == NULL)
149 		return (NULL);
150 
151 	str = mdb_zalloc(dp->dtdo_strlen + 1, UM_SLEEP | UM_GC);
152 
153 	for (i = 0; i == 0 || str[i - 1] != '\0'; i++, addr++) {
154 		if (mdb_vread(&str[i], sizeof (char), (uintptr_t)addr) == -1)
155 			return ("<unreadable>");
156 	}
157 
158 	return (str);
159 }
160 
161 static uint_t
162 dis_scope(const char *name)
163 {
164 	switch (name[2]) {
165 	case 'l': return (DIFV_SCOPE_LOCAL);
166 	case 't': return (DIFV_SCOPE_THREAD);
167 	case 'g': return (DIFV_SCOPE_GLOBAL);
168 	default: return (-1u);
169 	}
170 }
171 
172 static void
173 dis_lda(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
174 {
175 	uint_t var = DIF_INSTR_R1(instr);
176 	const char *vname;
177 
178 	mdb_printf("%-4s DIF_VAR(%x), %%r%u, %%r%u", name,
179 	    var, DIF_INSTR_R2(instr), DIF_INSTR_RD(instr));
180 
181 	if ((vname = dis_varname(dp, var, dis_scope(name))) != NULL)
182 		mdb_printf("\t\t! %s", vname);
183 }
184 
185 static void
186 dis_ldv(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
187 {
188 	uint_t var = DIF_INSTR_VAR(instr);
189 	const char *vname;
190 
191 	mdb_printf("%-4s DIF_VAR(%x), %%r%u", name, var, DIF_INSTR_RD(instr));
192 
193 	if ((vname = dis_varname(dp, var, dis_scope(name))) != NULL)
194 		mdb_printf("\t\t! %s", vname);
195 }
196 
197 static void
198 dis_stv(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
199 {
200 	uint_t var = DIF_INSTR_VAR(instr);
201 	const char *vname;
202 
203 	mdb_printf("%-4s %%r%u, DIF_VAR(%x)", name, DIF_INSTR_RS(instr), var);
204 
205 	if ((vname = dis_varname(dp, var, dis_scope(name))) != NULL)
206 		mdb_printf("\t\t! %s", vname);
207 }
208 
209 static void
210 dis_setx(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
211 {
212 	uint_t intptr = DIF_INSTR_INTEGER(instr);
213 
214 	mdb_printf("%-4s DIF_INTEGER[%u], %%r%u", name,
215 	    intptr, DIF_INSTR_RD(instr));
216 
217 	if (dp != NULL && intptr < dp->dtdo_intlen) {
218 		uint64_t *ip = mdb_alloc(dp->dtdo_intlen *
219 		    sizeof (uint64_t), UM_SLEEP | UM_GC);
220 
221 		if (mdb_vread(ip, dp->dtdo_intlen * sizeof (uint64_t),
222 		    (uintptr_t)dp->dtdo_inttab) == -1)
223 			mdb_warn("failed to read data at %p", dp->dtdo_inttab);
224 		else
225 			mdb_printf("\t\t! 0x%llx", ip[intptr]);
226 	}
227 }
228 
229 static void
230 dis_sets(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
231 {
232 	uint_t strptr = DIF_INSTR_STRING(instr);
233 
234 	mdb_printf("%-4s DIF_STRING[%u], %%r%u", name,
235 	    strptr, DIF_INSTR_RD(instr));
236 
237 	if (dp != NULL && strptr < dp->dtdo_strlen) {
238 		char *str = mdb_alloc(dp->dtdo_strlen, UM_SLEEP | UM_GC);
239 
240 		if (mdb_vread(str, dp->dtdo_strlen,
241 		    (uintptr_t)dp->dtdo_strtab) == -1)
242 			mdb_warn("failed to read data at %p", dp->dtdo_strtab);
243 		else
244 			mdb_printf("\t\t! \"%s\"", str + strptr);
245 	}
246 }
247 
248 /*ARGSUSED*/
249 static void
250 dis_ret(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
251 {
252 	mdb_printf("%-4s %%r%u", name, DIF_INSTR_RD(instr));
253 }
254 
255 /*ARGSUSED*/
256 static void
257 dis_call(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
258 {
259 	static struct {
260 		const char *name;
261 		int subr;
262 	} snames[] = {
263 		{ "rand",			DIF_SUBR_RAND },
264 		{ "mutex_owned",		DIF_SUBR_MUTEX_OWNED },
265 		{ "mutex_owner",		DIF_SUBR_MUTEX_OWNER },
266 		{ "mutex_type_adaptive",	DIF_SUBR_MUTEX_TYPE_ADAPTIVE },
267 		{ "mutex_type_spin",		DIF_SUBR_MUTEX_TYPE_SPIN },
268 		{ "rw_read_held",		DIF_SUBR_RW_READ_HELD },
269 		{ "rw_write_held",		DIF_SUBR_RW_WRITE_HELD },
270 		{ "rw_iswriter",		DIF_SUBR_RW_ISWRITER },
271 		{ "copyin",			DIF_SUBR_COPYIN },
272 		{ "copyinstr",			DIF_SUBR_COPYINSTR },
273 		{ "speculation",		DIF_SUBR_SPECULATION },
274 		{ "progenyof",			DIF_SUBR_PROGENYOF },
275 		{ "strlen",			DIF_SUBR_STRLEN },
276 		{ "copyout",			DIF_SUBR_COPYOUT },
277 		{ "copyoutstr",			DIF_SUBR_COPYOUTSTR },
278 		{ "alloca",			DIF_SUBR_ALLOCA },
279 		{ "bcopy",			DIF_SUBR_BCOPY },
280 		{ "copyinto",			DIF_SUBR_COPYINTO },
281 		{ "msgdsize",			DIF_SUBR_MSGDSIZE },
282 		{ "msgsize",			DIF_SUBR_MSGSIZE },
283 		{ "getmajor",			DIF_SUBR_GETMAJOR },
284 		{ "getminor",			DIF_SUBR_GETMINOR },
285 		{ "ddi_pathname",		DIF_SUBR_DDI_PATHNAME },
286 		{ "strjoin",			DIF_SUBR_STRJOIN },
287 		{ "lltostr",			DIF_SUBR_LLTOSTR },
288 		{ "basename",			DIF_SUBR_BASENAME },
289 		{ "dirname",			DIF_SUBR_DIRNAME },
290 		{ "cleanpath",			DIF_SUBR_CLEANPATH },
291 		{ "strchr",			DIF_SUBR_STRCHR },
292 		{ "strrchr",			DIF_SUBR_STRRCHR },
293 		{ "strstr",			DIF_SUBR_STRSTR },
294 		{ "strtok",			DIF_SUBR_STRTOK },
295 		{ "substr",			DIF_SUBR_SUBSTR },
296 		{ "index",			DIF_SUBR_INDEX },
297 		{ "rindex",			DIF_SUBR_RINDEX },
298 		{ NULL, 0 }
299 	};
300 
301 	uint_t subr = DIF_INSTR_SUBR(instr), i;
302 
303 	mdb_printf("%-4s DIF_SUBR(%u), %%r%u", name, subr, DIF_INSTR_RD(instr));
304 
305 	for (i = 0; snames[i].name != NULL; i++) {
306 		if (subr == snames[i].subr) {
307 			mdb_printf("\t\t! %s", snames[i].name);
308 			return;
309 		}
310 	}
311 }
312 
313 /*ARGSUSED*/
314 static void
315 dis_pushts(const dtrace_difo_t *dp, const char *name, dif_instr_t instr)
316 {
317 	static const char *const tnames[] = { "TYPE_CTF", "TYPE_STRING" };
318 	uint_t type = DIF_INSTR_TYPE(instr);
319 
320 	mdb_printf("%-4s DIF_TYPE(%u), %%r%u, %%r%u",
321 	    name, type, DIF_INSTR_R2(instr), DIF_INSTR_RS(instr));
322 
323 	if (type < sizeof (tnames) / sizeof (tnames[0]))
324 		mdb_printf("\t! %s", tnames[type]);
325 }
326 
327 static char *
328 dis_typestr(const dtrace_diftype_t *t, char *buf, size_t len)
329 {
330 	char kind[8];
331 
332 	switch (t->dtdt_kind) {
333 	case DIF_TYPE_CTF:
334 		(void) strcpy(kind, "D type");
335 		break;
336 	case DIF_TYPE_STRING:
337 		(void) strcpy(kind, "string");
338 		break;
339 	default:
340 		(void) mdb_snprintf(kind, sizeof (kind), "0x%x", t->dtdt_kind);
341 	}
342 
343 	if (t->dtdt_flags & DIF_TF_BYREF) {
344 		(void) mdb_snprintf(buf, len,
345 		    "%s by ref (size %lu)",
346 		    kind, (ulong_t)t->dtdt_size);
347 	} else {
348 		(void) mdb_snprintf(buf, len, "%s (size %lu)",
349 		    kind, (ulong_t)t->dtdt_size);
350 	}
351 
352 	return (buf);
353 }
354 
355 static int
356 dis(uintptr_t addr, dtrace_difo_t *dp)
357 {
358 	static const struct opent {
359 		const char *op_name;
360 		void (*op_func)(const dtrace_difo_t *,
361 		    const char *, dif_instr_t);
362 	} optab[] = {
363 		{ "(illegal opcode)", dis_str },
364 		{ "or", dis_log },		/* DIF_OP_OR */
365 		{ "xor", dis_log },		/* DIF_OP_XOR */
366 		{ "and", dis_log },		/* DIF_OP_AND */
367 		{ "sll", dis_log },		/* DIF_OP_SLL */
368 		{ "srl", dis_log },		/* DIF_OP_SRL */
369 		{ "sub", dis_log },		/* DIF_OP_SUB */
370 		{ "add", dis_log },		/* DIF_OP_ADD */
371 		{ "mul", dis_log },		/* DIF_OP_MUL */
372 		{ "sdiv", dis_log },		/* DIF_OP_SDIV */
373 		{ "udiv", dis_log },		/* DIF_OP_UDIV */
374 		{ "srem", dis_log },		/* DIF_OP_SREM */
375 		{ "urem", dis_log },		/* DIF_OP_UREM */
376 		{ "not", dis_r1rd },		/* DIF_OP_NOT */
377 		{ "mov", dis_r1rd },		/* DIF_OP_MOV */
378 		{ "cmp", dis_cmp },		/* DIF_OP_CMP */
379 		{ "tst", dis_tst },		/* DIF_OP_TST */
380 		{ "ba", dis_branch },		/* DIF_OP_BA */
381 		{ "be", dis_branch },		/* DIF_OP_BE */
382 		{ "bne", dis_branch },		/* DIF_OP_BNE */
383 		{ "bg", dis_branch },		/* DIF_OP_BG */
384 		{ "bgu", dis_branch },		/* DIF_OP_BGU */
385 		{ "bge", dis_branch },		/* DIF_OP_BGE */
386 		{ "bgeu", dis_branch },		/* DIF_OP_BGEU */
387 		{ "bl", dis_branch },		/* DIF_OP_BL */
388 		{ "blu", dis_branch },		/* DIF_OP_BLU */
389 		{ "ble", dis_branch },		/* DIF_OP_BLE */
390 		{ "bleu", dis_branch },		/* DIF_OP_BLEU */
391 		{ "ldsb", dis_load },		/* DIF_OP_LDSB */
392 		{ "ldsh", dis_load },		/* DIF_OP_LDSH */
393 		{ "ldsw", dis_load },		/* DIF_OP_LDSW */
394 		{ "ldub", dis_load },		/* DIF_OP_LDUB */
395 		{ "lduh", dis_load },		/* DIF_OP_LDUH */
396 		{ "lduw", dis_load },		/* DIF_OP_LDUW */
397 		{ "ldx", dis_load },		/* DIF_OP_LDX */
398 		{ "ret", dis_ret },		/* DIF_OP_RET */
399 		{ "nop", dis_str },		/* DIF_OP_NOP */
400 		{ "setx", dis_setx },		/* DIF_OP_SETX */
401 		{ "sets", dis_sets },		/* DIF_OP_SETS */
402 		{ "scmp", dis_cmp },		/* DIF_OP_SCMP */
403 		{ "ldga", dis_lda },		/* DIF_OP_LDGA */
404 		{ "ldgs", dis_ldv },		/* DIF_OP_LDGS */
405 		{ "stgs", dis_stv },		/* DIF_OP_STGS */
406 		{ "ldta", dis_lda },		/* DIF_OP_LDTA */
407 		{ "ldts", dis_ldv },		/* DIF_OP_LDTS */
408 		{ "stts", dis_stv },		/* DIF_OP_STTS */
409 		{ "sra", dis_log },		/* DIF_OP_SRA */
410 		{ "call", dis_call },		/* DIF_OP_CALL */
411 		{ "pushtr", dis_pushts },	/* DIF_OP_PUSHTR */
412 		{ "pushtv", dis_pushts },	/* DIF_OP_PUSHTV */
413 		{ "popts", dis_str },		/* DIF_OP_POPTS */
414 		{ "flushts", dis_str },		/* DIF_OP_FLUSHTS */
415 		{ "ldgaa", dis_ldv },		/* DIF_OP_LDGAA */
416 		{ "ldtaa", dis_ldv },		/* DIF_OP_LDTAA */
417 		{ "stgaa", dis_stv },		/* DIF_OP_STGAA */
418 		{ "sttaa", dis_stv },		/* DIF_OP_STTAA */
419 		{ "ldls", dis_ldv },		/* DIF_OP_LDLS */
420 		{ "stls", dis_stv },		/* DIF_OP_STLS */
421 		{ "allocs", dis_r1rd },		/* DIF_OP_ALLOCS */
422 		{ "copys", dis_log },		/* DIF_OP_COPYS */
423 		{ "stb", dis_store },		/* DIF_OP_STB */
424 		{ "sth", dis_store },		/* DIF_OP_STH */
425 		{ "stw", dis_store },		/* DIF_OP_STW */
426 		{ "stx", dis_store },		/* DIF_OP_STX */
427 		{ "uldsb", dis_load },		/* DIF_OP_ULDSB */
428 		{ "uldsh", dis_load },		/* DIF_OP_ULDSH */
429 		{ "uldsw", dis_load },		/* DIF_OP_ULDSW */
430 		{ "uldub", dis_load },		/* DIF_OP_ULDUB */
431 		{ "ulduh", dis_load },		/* DIF_OP_ULDUH */
432 		{ "ulduw", dis_load },		/* DIF_OP_ULDUW */
433 		{ "uldx", dis_load },		/* DIF_OP_ULDX */
434 		{ "rldsb", dis_load },		/* DIF_OP_RLDSB */
435 		{ "rldsh", dis_load },		/* DIF_OP_RLDSH */
436 		{ "rldsw", dis_load },		/* DIF_OP_RLDSW */
437 		{ "rldub", dis_load },		/* DIF_OP_RLDUB */
438 		{ "rlduh", dis_load },		/* DIF_OP_RLDUH */
439 		{ "rlduw", dis_load },		/* DIF_OP_RLDUW */
440 		{ "rldx", dis_load },		/* DIF_OP_RLDX */
441 	};
442 
443 	dif_instr_t instr, opcode;
444 	const struct opent *op;
445 
446 	if (mdb_vread(&instr, sizeof (dif_instr_t), addr) == -1) {
447 		mdb_warn("failed to read DIF instruction at %p", addr);
448 		return (DCMD_ERR);
449 	}
450 
451 	opcode = DIF_INSTR_OP(instr);
452 
453 	if (opcode >= sizeof (optab) / sizeof (optab[0]))
454 		opcode = 0; /* force invalid opcode message */
455 
456 	op = &optab[opcode];
457 	mdb_printf("%0*p %08x ", DIFO_ADDRWIDTH, addr, instr);
458 	op->op_func(dp, op->op_name, instr);
459 	mdb_printf("\n");
460 	mdb_set_dot(addr + sizeof (dif_instr_t));
461 
462 	return (DCMD_OK);
463 }
464 
465 /*ARGSUSED*/
466 int
467 difo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
468 {
469 	dtrace_difo_t difo, *dp = &difo;
470 	uintptr_t instr, limit;
471 	dtrace_difv_t *dvp;
472 	size_t varsize;
473 	ulong_t i;
474 	char type[64];
475 	char *str;
476 
477 	if (!(flags & DCMD_ADDRSPEC))
478 		return (DCMD_USAGE);
479 
480 	if (mdb_vread(dp, sizeof (dtrace_difo_t), addr) == -1) {
481 		mdb_warn("couldn't read dtrace_difo_t at %p", addr);
482 		return (DCMD_ERR);
483 	}
484 
485 	mdb_printf("%<u>DIF Object 0x%p%</u> (refcnt=%d)\n\n",
486 	    addr, dp->dtdo_refcnt);
487 	mdb_printf("%<b>%-*s %-8s %s%</b>\n", DIFO_ADDRWIDTH, "ADDR",
488 	    "OPCODE", "INSTRUCTION");
489 
490 	mdb_set_dot((uintmax_t)(uintptr_t)dp->dtdo_buf);
491 	limit = (uintptr_t)dp->dtdo_buf + dp->dtdo_len * sizeof (dif_instr_t);
492 
493 	while ((instr = mdb_get_dot()) < limit)
494 		dis(instr, dp);
495 
496 	if (dp->dtdo_varlen != 0) {
497 		mdb_printf("\n%<b>%-16s %-4s %-3s %-3s %-4s %s%</b>\n",
498 		    "NAME", "ID", "KND", "SCP", "FLAG", "TYPE");
499 	}
500 
501 	varsize = sizeof (dtrace_difv_t) * dp->dtdo_varlen;
502 	dvp = mdb_alloc(varsize, UM_SLEEP | UM_GC);
503 
504 	if (mdb_vread(dvp, varsize, (uintptr_t)dp->dtdo_vartab) == -1) {
505 		mdb_warn("couldn't read dtdo_vartab");
506 		return (DCMD_ERR);
507 	}
508 
509 	str = mdb_alloc(dp->dtdo_strlen, UM_SLEEP | UM_GC);
510 
511 	if (mdb_vread(str, dp->dtdo_strlen, (uintptr_t)dp->dtdo_strtab) == -1) {
512 		mdb_warn("couldn't read dtdo_strtab");
513 		return (DCMD_ERR);
514 	}
515 
516 	for (i = 0; i < dp->dtdo_varlen; i++) {
517 		dtrace_difv_t *v = &dvp[i];
518 		char kind[4], scope[4], flags[16] = { 0 };
519 
520 		switch (v->dtdv_kind) {
521 		case DIFV_KIND_ARRAY:
522 			(void) strcpy(kind, "arr");
523 			break;
524 		case DIFV_KIND_SCALAR:
525 			(void) strcpy(kind, "scl");
526 			break;
527 		default:
528 			(void) mdb_snprintf(kind, sizeof (kind),
529 			    "%u", v->dtdv_kind);
530 		}
531 
532 		switch (v->dtdv_scope) {
533 		case DIFV_SCOPE_GLOBAL:
534 			(void) strcpy(scope, "glb");
535 			break;
536 		case DIFV_SCOPE_THREAD:
537 			(void) strcpy(scope, "tls");
538 			break;
539 		case DIFV_SCOPE_LOCAL:
540 			(void) strcpy(scope, "loc");
541 			break;
542 		default:
543 			(void) mdb_snprintf(scope, sizeof (scope),
544 			    "%u", v->dtdv_scope);
545 		}
546 
547 		if (v->dtdv_flags & ~(DIFV_F_REF | DIFV_F_MOD)) {
548 			(void) mdb_snprintf(flags, sizeof (flags), "/0x%x",
549 			    v->dtdv_flags & ~(DIFV_F_REF | DIFV_F_MOD));
550 		}
551 
552 		if (v->dtdv_flags & DIFV_F_REF)
553 			(void) strcat(flags, "/r");
554 		if (v->dtdv_flags & DIFV_F_MOD)
555 			(void) strcat(flags, "/w");
556 
557 		mdb_printf("%-16s %-4x %-3s %-3s %-4s %s\n",
558 		    &str[v->dtdv_name],
559 		    v->dtdv_id, kind, scope, flags + 1,
560 		    dis_typestr(&v->dtdv_type, type, sizeof (type)));
561 	}
562 
563 	mdb_printf("\n%<b>RETURN%</b>\n%s\n\n",
564 	    dis_typestr(&dp->dtdo_rtype, type, sizeof (type)));
565 
566 	return (DCMD_OK);
567 }
568 
569 /*ARGSUSED*/
570 int
571 difinstr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
572 {
573 	if (!(flags & DCMD_ADDRSPEC))
574 		return (DCMD_USAGE);
575 
576 	return (dis(addr, NULL));
577 }
578 
579 /*ARGSUSED*/
580 int
581 id2probe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
582 {
583 	uintptr_t probe = NULL;
584 	uintptr_t probes;
585 
586 	if (!(flags & DCMD_ADDRSPEC))
587 		return (DCMD_USAGE);
588 
589 	if (addr == DTRACE_IDNONE || addr > UINT32_MAX)
590 		goto out;
591 
592 	if (mdb_readvar(&probes, "dtrace_probes") == -1) {
593 		mdb_warn("failed to read 'dtrace_probes'");
594 		return (DCMD_ERR);
595 	}
596 
597 	probes += (addr - 1) * sizeof (dtrace_probe_t *);
598 
599 	if (mdb_vread(&probe, sizeof (uintptr_t), probes) == -1) {
600 		mdb_warn("failed to read dtrace_probes[%d]", addr - 1);
601 		return (DCMD_ERR);
602 	}
603 
604 out:
605 	mdb_printf("%p\n", probe);
606 	return (DCMD_OK);
607 }
608 
609 /*ARGSUSED*/
610 int
611 dof_hdr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
612 {
613 	dof_hdr_t h;
614 
615 	if (argc != 0)
616 		return (DCMD_USAGE);
617 
618 	if (!(flags & DCMD_ADDRSPEC))
619 		addr = 0; /* assume base of file in file target */
620 
621 	if (mdb_vread(&h, sizeof (h), addr) != sizeof (h)) {
622 		mdb_warn("failed to read header at %p", addr);
623 		return (DCMD_ERR);
624 	}
625 
626 	mdb_printf("dofh_ident.id_magic = 0x%x, %c, %c, %c\n",
627 	    h.dofh_ident[DOF_ID_MAG0], h.dofh_ident[DOF_ID_MAG1],
628 	    h.dofh_ident[DOF_ID_MAG2], h.dofh_ident[DOF_ID_MAG3]);
629 
630 	switch (h.dofh_ident[DOF_ID_MODEL]) {
631 	case DOF_MODEL_ILP32:
632 		mdb_printf("dofh_ident.id_model = ILP32\n");
633 		break;
634 	case DOF_MODEL_LP64:
635 		mdb_printf("dofh_ident.id_model = LP64\n");
636 		break;
637 	default:
638 		mdb_printf("dofh_ident.id_model = 0x%x\n",
639 		    h.dofh_ident[DOF_ID_MODEL]);
640 	}
641 
642 	switch (h.dofh_ident[DOF_ID_ENCODING]) {
643 	case DOF_ENCODE_LSB:
644 		mdb_printf("dofh_ident.id_encoding = LSB\n");
645 		break;
646 	case DOF_ENCODE_MSB:
647 		mdb_printf("dofh_ident.id_encoding = MSB\n");
648 		break;
649 	default:
650 		mdb_printf("dofh_ident.id_encoding = 0x%x\n",
651 		    h.dofh_ident[DOF_ID_ENCODING]);
652 	}
653 
654 	mdb_printf("dofh_ident.id_version = %u\n",
655 	    h.dofh_ident[DOF_ID_VERSION]);
656 	mdb_printf("dofh_ident.id_difvers = %u\n",
657 	    h.dofh_ident[DOF_ID_DIFVERS]);
658 	mdb_printf("dofh_ident.id_difireg = %u\n",
659 	    h.dofh_ident[DOF_ID_DIFIREG]);
660 	mdb_printf("dofh_ident.id_diftreg = %u\n",
661 	    h.dofh_ident[DOF_ID_DIFTREG]);
662 
663 	mdb_printf("dofh_flags = 0x%x\n", h.dofh_flags);
664 	mdb_printf("dofh_hdrsize = %u\n", h.dofh_hdrsize);
665 	mdb_printf("dofh_secsize = %u\n", h.dofh_secsize);
666 	mdb_printf("dofh_secnum = %u\n", h.dofh_secnum);
667 	mdb_printf("dofh_secoff = %llu\n", h.dofh_secoff);
668 	mdb_printf("dofh_loadsz = %llu\n", h.dofh_loadsz);
669 	mdb_printf("dofh_filesz = %llu\n", h.dofh_filesz);
670 
671 	return (DCMD_OK);
672 }
673 
674 /*ARGSUSED*/
675 static int
676 dof_sec_walk(uintptr_t addr, void *ignored, int *sec)
677 {
678 	mdb_printf("%3d ", (*sec)++);
679 	(void) dof_sec(addr, DCMD_ADDRSPEC | DCMD_LOOP, 0, NULL);
680 	return (WALK_NEXT);
681 }
682 
683 static const char *
684 dof_sec_typename(uint32_t type)
685 {
686 	static const char *const types[] = {
687 		"none", "comments", "source", "ecbdesc", "probedesc", "actdesc",
688 		"difohdr", "dif", "strtab", "vartab", "reltab", "typtab",
689 		"urelhdr", "krelhdr", "optdesc", "provider", "probes",
690 		"prargs", "proffs", "inttab", "utsname"
691 	};
692 	static char buf[32];
693 
694 	if (type < sizeof (types) / sizeof (types[0]))
695 		return (types[type]);
696 
697 	mdb_snprintf(buf, sizeof (buf), "%u", type);
698 	return (buf);
699 }
700 
701 /*ARGSUSED*/
702 int
703 dof_sec(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
704 {
705 	dof_sec_t s;
706 
707 	if (!(flags & DCMD_ADDRSPEC))
708 		mdb_printf("%<u>%-3s ", "NDX");
709 
710 	if (!(flags & DCMD_ADDRSPEC) || DCMD_HDRSPEC(flags)) {
711 		mdb_printf("%<u>%?s %-10s %-5s %-5s %-5s %-6s %-5s%</u>\n",
712 		    "ADDR", "TYPE", "ALIGN", "FLAGS", "ENTSZ", "OFFSET",
713 		    "SIZE");
714 	}
715 
716 	if (!(flags & DCMD_ADDRSPEC)) {
717 		int sec = 0;
718 
719 		if (mdb_walk("dof_sec",
720 		    (mdb_walk_cb_t)dof_sec_walk, &sec) == -1) {
721 			mdb_warn("failed to walk dof_sec");
722 			return (DCMD_ERR);
723 		}
724 		return (DCMD_OK);
725 	}
726 
727 	if (argc != 0)
728 		return (DCMD_USAGE);
729 
730 	if (mdb_vread(&s, sizeof (s), addr) != sizeof (s)) {
731 		mdb_warn("failed to read section header at %p", addr);
732 		return (DCMD_ERR);
733 	}
734 
735 	mdb_printf("%?p ", addr);
736 	mdb_printf("%-10s ", dof_sec_typename(s.dofs_type));
737 
738 	mdb_printf("%-5u %-#5x %-#5x %-6llx %-#5llx\n", s.dofs_align,
739 	    s.dofs_flags, s.dofs_entsize, s.dofs_offset, s.dofs_size);
740 
741 	return (DCMD_OK);
742 }
743 
744 int
745 dof_sec_walk_init(mdb_walk_state_t *wsp)
746 {
747 	dof_hdr_t h, *hp;
748 	size_t size;
749 
750 	if (mdb_vread(&h, sizeof (h), wsp->walk_addr) != sizeof (h)) {
751 		mdb_warn("failed to read DOF header at %p", wsp->walk_addr);
752 		return (WALK_ERR);
753 	}
754 
755 	size = sizeof (dof_hdr_t) + sizeof (dof_sec_t) * h.dofh_secnum;
756 	hp = mdb_alloc(size, UM_SLEEP);
757 
758 	if (mdb_vread(hp, size, wsp->walk_addr) != size) {
759 		mdb_warn("failed to read DOF sections at %p", wsp->walk_addr);
760 		mdb_free(hp, size);
761 		return (WALK_ERR);
762 	}
763 
764 	wsp->walk_arg = (void *)0;
765 	wsp->walk_data = hp;
766 
767 	return (WALK_NEXT);
768 }
769 
770 int
771 dof_sec_walk_step(mdb_walk_state_t *wsp)
772 {
773 	uint_t i = (uintptr_t)wsp->walk_arg;
774 	size_t off = sizeof (dof_hdr_t) + sizeof (dof_sec_t) * i;
775 	dof_hdr_t *hp = wsp->walk_data;
776 	dof_sec_t *sp = (dof_sec_t *)((uintptr_t)hp + off);
777 
778 	if (i >= hp->dofh_secnum)
779 		return (WALK_DONE);
780 
781 	wsp->walk_arg = (void *)(uintptr_t)(i + 1);
782 	return (wsp->walk_callback(wsp->walk_addr + off, sp, wsp->walk_cbdata));
783 }
784 
785 void
786 dof_sec_walk_fini(mdb_walk_state_t *wsp)
787 {
788 	dof_hdr_t *hp = wsp->walk_data;
789 	mdb_free(hp, sizeof (dof_hdr_t) + sizeof (dof_sec_t) * hp->dofh_secnum);
790 }
791 
792 /*ARGSUSED*/
793 int
794 dof_ecbdesc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
795 {
796 	dof_ecbdesc_t e;
797 
798 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
799 		return (DCMD_USAGE);
800 
801 	if (mdb_vread(&e, sizeof (e), addr) != sizeof (e)) {
802 		mdb_warn("failed to read ecbdesc at %p", addr);
803 		return (DCMD_ERR);
804 	}
805 
806 	mdb_printf("dofe_probes = %d\n", e.dofe_probes);
807 	mdb_printf("dofe_actions = %d\n", e.dofe_actions);
808 	mdb_printf("dofe_pred = %d\n", e.dofe_pred);
809 	mdb_printf("dofe_uarg = 0x%llx\n", e.dofe_uarg);
810 
811 	return (DCMD_OK);
812 }
813 
814 /*ARGSUSED*/
815 int
816 dof_probedesc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
817 {
818 	dof_probedesc_t p;
819 
820 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
821 		return (DCMD_USAGE);
822 
823 	if (mdb_vread(&p, sizeof (p), addr) != sizeof (p)) {
824 		mdb_warn("failed to read probedesc at %p", addr);
825 		return (DCMD_ERR);
826 	}
827 
828 	mdb_printf("dofp_strtab = %d\n", p.dofp_strtab);
829 	mdb_printf("dofp_provider = %u\n", p.dofp_provider);
830 	mdb_printf("dofp_mod = %u\n", p.dofp_mod);
831 	mdb_printf("dofp_func = %u\n", p.dofp_func);
832 	mdb_printf("dofp_name = %u\n", p.dofp_name);
833 	mdb_printf("dofp_id = %u\n", p.dofp_id);
834 
835 	return (DCMD_OK);
836 }
837 
838 /*ARGSUSED*/
839 int
840 dof_actdesc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
841 {
842 	dof_actdesc_t a;
843 
844 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
845 		return (DCMD_USAGE);
846 
847 	if (mdb_vread(&a, sizeof (a), addr) != sizeof (a)) {
848 		mdb_warn("failed to read actdesc at %p", addr);
849 		return (DCMD_ERR);
850 	}
851 
852 	mdb_printf("dofa_difo = %d\n", a.dofa_difo);
853 	mdb_printf("dofa_strtab = %d\n", a.dofa_strtab);
854 	mdb_printf("dofa_kind = %u\n", a.dofa_kind);
855 	mdb_printf("dofa_ntuple = %u\n", a.dofa_ntuple);
856 	mdb_printf("dofa_arg = 0x%llx\n", a.dofa_arg);
857 	mdb_printf("dofa_uarg = 0x%llx\n", a.dofa_uarg);
858 
859 	return (DCMD_OK);
860 }
861 
862 /*ARGSUSED*/
863 int
864 dof_relohdr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
865 {
866 	dof_relohdr_t r;
867 
868 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
869 		return (DCMD_USAGE);
870 
871 	if (mdb_vread(&r, sizeof (r), addr) != sizeof (r)) {
872 		mdb_warn("failed to read relohdr at %p", addr);
873 		return (DCMD_ERR);
874 	}
875 
876 	mdb_printf("dofr_strtab = %d\n", r.dofr_strtab);
877 	mdb_printf("dofr_relsec = %d\n", r.dofr_relsec);
878 	mdb_printf("dofr_tgtsec = %d\n", r.dofr_tgtsec);
879 
880 	return (DCMD_OK);
881 }
882 
883 /*ARGSUSED*/
884 int
885 dof_relodesc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
886 {
887 	dof_relodesc_t r;
888 
889 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
890 		return (DCMD_USAGE);
891 
892 	if (mdb_vread(&r, sizeof (r), addr) != sizeof (r)) {
893 		mdb_warn("failed to read relodesc at %p", addr);
894 		return (DCMD_ERR);
895 	}
896 
897 	mdb_printf("dofr_name = %u\n", r.dofr_name);
898 	mdb_printf("dofr_type = %u\n", r.dofr_type);
899 	mdb_printf("dofr_offset = 0x%llx\n", r.dofr_offset);
900 	mdb_printf("dofr_data = 0x%llx\n", r.dofr_data);
901 
902 	return (DCMD_OK);
903 }
904 
905 void
906 dtrace_help(void)
907 {
908 
909 	mdb_printf("Given a dtrace_state_t structure that represents a "
910 	    "DTrace consumer, prints\n"
911 	    "dtrace(1M)-like output for in-kernel DTrace data.  (The "
912 	    "dtrace_state_t\n"
913 	    "structures for all DTrace consumers may be obtained by running "
914 	    "the \n"
915 	    "::dtrace_state dcmd.)   When data is present on multiple CPUs, "
916 	    "data are\n"
917 	    "presented in CPU order, with records within each CPU ordered "
918 	    "oldest to \n"
919 	    "youngest.  Options:\n\n"
920 	    "-c cpu     Only provide output for specified CPU.\n");
921 }
922 
923 static int
924 dtracemdb_eprobe(dtrace_state_t *state, dtrace_eprobedesc_t *epd)
925 {
926 	dtrace_epid_t epid = epd->dtepd_epid;
927 	dtrace_probe_t probe;
928 	dtrace_ecb_t ecb;
929 	uintptr_t addr, paddr, ap;
930 	dtrace_action_t act;
931 	int nactions, nrecs;
932 
933 	addr = (uintptr_t)state->dts_ecbs +
934 	    (epid - 1) * sizeof (dtrace_ecb_t *);
935 
936 	if (mdb_vread(&addr, sizeof (addr), addr) == -1) {
937 		mdb_warn("failed to read ecb for epid %d", epid);
938 		return (-1);
939 	}
940 
941 	if (addr == NULL) {
942 		mdb_warn("epid %d doesn't match an ecb\n", epid);
943 		return (-1);
944 	}
945 
946 	if (mdb_vread(&ecb, sizeof (ecb), addr) == -1) {
947 		mdb_warn("failed to read ecb at %p", addr);
948 		return (-1);
949 	}
950 
951 	paddr = (uintptr_t)ecb.dte_probe;
952 
953 	if (mdb_vread(&probe, sizeof (probe), paddr) == -1) {
954 		mdb_warn("failed to read probe for ecb %p", addr);
955 		return (-1);
956 	}
957 
958 	/*
959 	 * This is a little painful:  in order to find the number of actions,
960 	 * we need to first walk through them.
961 	 */
962 	for (ap = (uintptr_t)ecb.dte_action, nactions = 0; ap != NULL; ) {
963 		if (mdb_vread(&act, sizeof (act), ap) == -1) {
964 			mdb_warn("failed to read action %p on ecb %p",
965 			    ap, addr);
966 			return (-1);
967 		}
968 
969 		if (!DTRACEACT_ISAGG(act.dta_kind) && !act.dta_intuple)
970 			nactions++;
971 
972 		ap = (uintptr_t)act.dta_next;
973 	}
974 
975 	nrecs = epd->dtepd_nrecs;
976 	epd->dtepd_nrecs = nactions;
977 	epd->dtepd_probeid = probe.dtpr_id;
978 	epd->dtepd_uarg = ecb.dte_uarg;
979 	epd->dtepd_size = ecb.dte_size;
980 
981 	for (ap = (uintptr_t)ecb.dte_action, nactions = 0; ap != NULL; ) {
982 		if (mdb_vread(&act, sizeof (act), ap) == -1) {
983 			mdb_warn("failed to read action %p on ecb %p",
984 			    ap, addr);
985 			return (-1);
986 		}
987 
988 		if (!DTRACEACT_ISAGG(act.dta_kind) && !act.dta_intuple) {
989 			if (nrecs-- == 0)
990 				break;
991 
992 			epd->dtepd_rec[nactions++] = act.dta_rec;
993 		}
994 
995 		ap = (uintptr_t)act.dta_next;
996 	}
997 
998 	return (0);
999 }
1000 
1001 /*ARGSUSED*/
1002 static int
1003 dtracemdb_probe(dtrace_state_t *state, dtrace_probedesc_t *pd)
1004 {
1005 	uintptr_t base, addr, paddr, praddr;
1006 	int nprobes, i;
1007 	dtrace_probe_t probe;
1008 	dtrace_provider_t prov;
1009 
1010 	if (pd->dtpd_id == DTRACE_IDNONE)
1011 		pd->dtpd_id++;
1012 
1013 	if (mdb_readvar(&base, "dtrace_probes") == -1) {
1014 		mdb_warn("failed to read 'dtrace_probes'");
1015 		return (-1);
1016 	}
1017 
1018 	if (mdb_readvar(&nprobes, "dtrace_nprobes") == -1) {
1019 		mdb_warn("failed to read 'dtrace_nprobes'");
1020 		return (-1);
1021 	}
1022 
1023 	for (i = pd->dtpd_id; i <= nprobes; i++) {
1024 		addr = base + (i - 1) * sizeof (dtrace_probe_t *);
1025 
1026 		if (mdb_vread(&paddr, sizeof (paddr), addr) == -1) {
1027 			mdb_warn("couldn't read probe pointer at %p", addr);
1028 			return (-1);
1029 		}
1030 
1031 		if (paddr != NULL)
1032 			break;
1033 	}
1034 
1035 	if (paddr == NULL) {
1036 		errno = ESRCH;
1037 		return (-1);
1038 	}
1039 
1040 	if (mdb_vread(&probe, sizeof (probe), paddr) == -1) {
1041 		mdb_warn("couldn't read probe at %p", paddr);
1042 		return (-1);
1043 	}
1044 
1045 	pd->dtpd_id = probe.dtpr_id;
1046 
1047 	if (mdb_vread(pd->dtpd_name, DTRACE_NAMELEN,
1048 	    (uintptr_t)probe.dtpr_name) == -1) {
1049 		mdb_warn("failed to read probe name for probe %p", paddr);
1050 		return (-1);
1051 	}
1052 
1053 	if (mdb_vread(pd->dtpd_func, DTRACE_FUNCNAMELEN,
1054 	    (uintptr_t)probe.dtpr_func) == -1) {
1055 		mdb_warn("failed to read function name for probe %p", paddr);
1056 		return (-1);
1057 	}
1058 
1059 	if (mdb_vread(pd->dtpd_mod, DTRACE_MODNAMELEN,
1060 	    (uintptr_t)probe.dtpr_mod) == -1) {
1061 		mdb_warn("failed to read module name for probe %p", paddr);
1062 		return (-1);
1063 	}
1064 
1065 	praddr = (uintptr_t)probe.dtpr_provider;
1066 
1067 	if (mdb_vread(&prov, sizeof (prov), praddr) == -1) {
1068 		mdb_warn("failed to read provider for probe %p", paddr);
1069 		return (-1);
1070 	}
1071 
1072 	if (mdb_vread(pd->dtpd_provider, DTRACE_PROVNAMELEN,
1073 	    (uintptr_t)prov.dtpv_name) == -1) {
1074 		mdb_warn("failed to read provider name for probe %p", paddr);
1075 		return (-1);
1076 	}
1077 
1078 	return (0);
1079 }
1080 
1081 /*ARGSUSED*/
1082 static int
1083 dtracemdb_aggdesc(dtrace_state_t *state, dtrace_aggdesc_t *agd)
1084 {
1085 	dtrace_aggid_t aggid = agd->dtagd_id;
1086 	dtrace_aggregation_t agg;
1087 	dtrace_ecb_t ecb;
1088 	uintptr_t addr, eaddr, ap, last;
1089 	dtrace_action_t act;
1090 	dtrace_recdesc_t *lrec;
1091 	int nactions, nrecs;
1092 
1093 	addr = (uintptr_t)state->dts_aggregations +
1094 	    (aggid - 1) * sizeof (dtrace_aggregation_t *);
1095 
1096 	if (mdb_vread(&addr, sizeof (addr), addr) == -1) {
1097 		mdb_warn("failed to read aggregation for aggid %d", aggid);
1098 		return (-1);
1099 	}
1100 
1101 	if (addr == NULL) {
1102 		mdb_warn("aggid %d doesn't match an aggregation\n", aggid);
1103 		return (-1);
1104 	}
1105 
1106 	if (mdb_vread(&agg, sizeof (agg), addr) == -1) {
1107 		mdb_warn("failed to read aggregation at %p", addr);
1108 		return (-1);
1109 	}
1110 
1111 	eaddr = (uintptr_t)agg.dtag_ecb;
1112 
1113 	if (mdb_vread(&ecb, sizeof (ecb), eaddr) == -1) {
1114 		mdb_warn("failed to read ecb for aggregation %p", addr);
1115 		return (-1);
1116 	}
1117 
1118 	last = (uintptr_t)addr + offsetof(dtrace_aggregation_t, dtag_action);
1119 
1120 	/*
1121 	 * This is a little painful:  in order to find the number of actions,
1122 	 * we need to first walk through them.
1123 	 */
1124 	ap = (uintptr_t)agg.dtag_first;
1125 	nactions = 0;
1126 
1127 	for (;;) {
1128 		if (mdb_vread(&act, sizeof (act), ap) == -1) {
1129 			mdb_warn("failed to read action %p on aggregation %p",
1130 			    ap, addr);
1131 			return (-1);
1132 		}
1133 
1134 		nactions++;
1135 
1136 		if (ap == last)
1137 			break;
1138 
1139 		ap = (uintptr_t)act.dta_next;
1140 	}
1141 
1142 	lrec = &act.dta_rec;
1143 	agd->dtagd_size = lrec->dtrd_offset + lrec->dtrd_size - agg.dtag_base;
1144 
1145 	nrecs = agd->dtagd_nrecs;
1146 	agd->dtagd_nrecs = nactions;
1147 	agd->dtagd_epid = ecb.dte_epid;
1148 
1149 	ap = (uintptr_t)agg.dtag_first;
1150 	nactions = 0;
1151 
1152 	for (;;) {
1153 		dtrace_recdesc_t rec;
1154 
1155 		if (mdb_vread(&act, sizeof (act), ap) == -1) {
1156 			mdb_warn("failed to read action %p on aggregation %p",
1157 			    ap, addr);
1158 			return (-1);
1159 		}
1160 
1161 		if (nrecs-- == 0)
1162 			break;
1163 
1164 		rec = act.dta_rec;
1165 		rec.dtrd_offset -= agg.dtag_base;
1166 		rec.dtrd_uarg = 0;
1167 		agd->dtagd_rec[nactions++] = rec;
1168 
1169 		if (ap == last)
1170 			break;
1171 
1172 		ap = (uintptr_t)act.dta_next;
1173 	}
1174 
1175 	return (0);
1176 }
1177 
1178 static int
1179 dtracemdb_bufsnap(dtrace_buffer_t *which, dtrace_bufdesc_t *desc)
1180 {
1181 	uintptr_t addr;
1182 	size_t bufsize;
1183 	dtrace_buffer_t buf;
1184 	caddr_t data = desc->dtbd_data;
1185 	processorid_t max_cpuid, cpu = desc->dtbd_cpu;
1186 
1187 	if (mdb_readvar(&max_cpuid, "max_cpuid") == -1) {
1188 		mdb_warn("failed to read 'max_cpuid'");
1189 		errno = EIO;
1190 		return (-1);
1191 	}
1192 
1193 	if (cpu < 0 || cpu > max_cpuid) {
1194 		errno = EINVAL;
1195 		return (-1);
1196 	}
1197 
1198 	addr = (uintptr_t)which + cpu * sizeof (dtrace_buffer_t);
1199 
1200 	if (mdb_vread(&buf, sizeof (buf), addr) == -1) {
1201 		mdb_warn("failed to read buffer description at %p", addr);
1202 		errno = EIO;
1203 		return (-1);
1204 	}
1205 
1206 	if (buf.dtb_tomax == NULL) {
1207 		errno = ENOENT;
1208 		return (-1);
1209 	}
1210 
1211 	if (buf.dtb_flags & DTRACEBUF_WRAPPED) {
1212 		bufsize = buf.dtb_size;
1213 	} else {
1214 		bufsize = buf.dtb_offset;
1215 	}
1216 
1217 	if (mdb_vread(data, bufsize, (uintptr_t)buf.dtb_tomax) == -1) {
1218 		mdb_warn("couldn't read buffer for CPU %d", cpu);
1219 		errno = EIO;
1220 		return (-1);
1221 	}
1222 
1223 	if (buf.dtb_offset > buf.dtb_size) {
1224 		mdb_warn("buffer for CPU %d has corrupt offset\n", cpu);
1225 		errno = EIO;
1226 		return (-1);
1227 	}
1228 
1229 	if (buf.dtb_flags & DTRACEBUF_WRAPPED) {
1230 		if (buf.dtb_xamot_offset > buf.dtb_size) {
1231 			mdb_warn("ringbuffer for CPU %d has corrupt "
1232 			    "wrapped offset\n", cpu);
1233 			errno = EIO;
1234 			return (-1);
1235 		}
1236 
1237 		/*
1238 		 * If the ring buffer has wrapped, it needs to be polished.
1239 		 * See the comment in dtrace_buffer_polish() for details.
1240 		 */
1241 		if (buf.dtb_offset < buf.dtb_xamot_offset) {
1242 			bzero(data + buf.dtb_offset,
1243 			    buf.dtb_xamot_offset - buf.dtb_offset);
1244 		}
1245 
1246 		if (buf.dtb_offset > buf.dtb_xamot_offset) {
1247 			bzero(data + buf.dtb_offset,
1248 			    buf.dtb_size - buf.dtb_offset);
1249 			bzero(data, buf.dtb_xamot_offset);
1250 		}
1251 
1252 		desc->dtbd_oldest = buf.dtb_xamot_offset;
1253 	} else {
1254 		desc->dtbd_oldest = 0;
1255 	}
1256 
1257 	desc->dtbd_size = bufsize;
1258 	desc->dtbd_drops = buf.dtb_drops;
1259 	desc->dtbd_errors = buf.dtb_errors;
1260 
1261 	return (0);
1262 }
1263 
1264 /*
1265  * This is essentially identical to its cousin in the kernel.
1266  */
1267 static dof_hdr_t *
1268 dtracemdb_dof_create(dtrace_state_t *state)
1269 {
1270 	dof_hdr_t *dof;
1271 	dof_sec_t *sec;
1272 	dof_optdesc_t *opt;
1273 	int i, len = sizeof (dof_hdr_t) +
1274 	    roundup(sizeof (dof_sec_t), sizeof (uint64_t)) +
1275 	    sizeof (dof_optdesc_t) * DTRACEOPT_MAX;
1276 
1277 	dof = mdb_zalloc(len, UM_SLEEP);
1278 	dof->dofh_ident[DOF_ID_MAG0] = DOF_MAG_MAG0;
1279 	dof->dofh_ident[DOF_ID_MAG1] = DOF_MAG_MAG1;
1280 	dof->dofh_ident[DOF_ID_MAG2] = DOF_MAG_MAG2;
1281 	dof->dofh_ident[DOF_ID_MAG3] = DOF_MAG_MAG3;
1282 
1283 	dof->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_NATIVE;
1284 	dof->dofh_ident[DOF_ID_ENCODING] = DOF_ENCODE_NATIVE;
1285 	dof->dofh_ident[DOF_ID_VERSION] = DOF_VERSION_1;
1286 	dof->dofh_ident[DOF_ID_DIFVERS] = DIF_VERSION;
1287 	dof->dofh_ident[DOF_ID_DIFIREG] = DIF_DIR_NREGS;
1288 	dof->dofh_ident[DOF_ID_DIFTREG] = DIF_DTR_NREGS;
1289 
1290 	dof->dofh_flags = 0;
1291 	dof->dofh_hdrsize = sizeof (dof_hdr_t);
1292 	dof->dofh_secsize = sizeof (dof_sec_t);
1293 	dof->dofh_secnum = 1;	/* only DOF_SECT_OPTDESC */
1294 	dof->dofh_secoff = sizeof (dof_hdr_t);
1295 	dof->dofh_loadsz = len;
1296 	dof->dofh_filesz = len;
1297 	dof->dofh_pad = 0;
1298 
1299 	/*
1300 	 * Fill in the option section header...
1301 	 */
1302 	sec = (dof_sec_t *)((uintptr_t)dof + sizeof (dof_hdr_t));
1303 	sec->dofs_type = DOF_SECT_OPTDESC;
1304 	sec->dofs_align = sizeof (uint64_t);
1305 	sec->dofs_flags = DOF_SECF_LOAD;
1306 	sec->dofs_entsize = sizeof (dof_optdesc_t);
1307 
1308 	opt = (dof_optdesc_t *)((uintptr_t)sec +
1309 	    roundup(sizeof (dof_sec_t), sizeof (uint64_t)));
1310 
1311 	sec->dofs_offset = (uintptr_t)opt - (uintptr_t)dof;
1312 	sec->dofs_size = sizeof (dof_optdesc_t) * DTRACEOPT_MAX;
1313 
1314 	for (i = 0; i < DTRACEOPT_MAX; i++) {
1315 		opt[i].dofo_option = i;
1316 		opt[i].dofo_strtab = DOF_SECIDX_NONE;
1317 		opt[i].dofo_value = state->dts_options[i];
1318 	}
1319 
1320 	return (dof);
1321 }
1322 
1323 static int
1324 dtracemdb_format(dtrace_state_t *state, dtrace_fmtdesc_t *desc)
1325 {
1326 	uintptr_t addr, faddr;
1327 	char c;
1328 	int len = 0;
1329 
1330 	if (desc->dtfd_format == 0 || desc->dtfd_format > state->dts_nformats) {
1331 		errno = EINVAL;
1332 		return (-1);
1333 	}
1334 
1335 	faddr = (uintptr_t)state->dts_formats +
1336 	    (desc->dtfd_format - 1) * sizeof (char *);
1337 
1338 	if (mdb_vread(&addr, sizeof (addr), faddr) == -1) {
1339 		mdb_warn("failed to read format string pointer at %p", faddr);
1340 		return (-1);
1341 	}
1342 
1343 	do {
1344 		if (mdb_vread(&c, sizeof (c), addr + len++) == -1) {
1345 			mdb_warn("failed to read format string at %p", addr);
1346 			return (-1);
1347 		}
1348 	} while (c != '\0');
1349 
1350 	if (len > desc->dtfd_length) {
1351 		desc->dtfd_length = len;
1352 		return (0);
1353 	}
1354 
1355 	if (mdb_vread(desc->dtfd_string, len, addr) == -1) {
1356 		mdb_warn("failed to reread format string at %p", addr);
1357 		return (-1);
1358 	}
1359 
1360 	return (0);
1361 }
1362 
1363 static int
1364 dtracemdb_status(dtrace_state_t *state, dtrace_status_t *status)
1365 {
1366 	dtrace_dstate_t *dstate;
1367 	int i, j;
1368 	uint64_t nerrs;
1369 	uintptr_t addr;
1370 	int ncpu;
1371 
1372 	if (mdb_readvar(&ncpu, "_ncpu") == -1) {
1373 		mdb_warn("failed to read '_ncpu'");
1374 		return (DCMD_ERR);
1375 	}
1376 
1377 	bzero(status, sizeof (dtrace_status_t));
1378 
1379 	if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) {
1380 		errno = ENOENT;
1381 		return (-1);
1382 	}
1383 
1384 	/*
1385 	 * For the MDB backend, we never set dtst_exiting or dtst_filled.  This
1386 	 * is by design:  we don't want the library to try to stop tracing,
1387 	 * because it doesn't particularly mean anything.
1388 	 */
1389 	nerrs = state->dts_errors;
1390 	dstate = &state->dts_vstate.dtvs_dynvars;
1391 
1392 	for (i = 0; i < ncpu; i++) {
1393 		dtrace_dstate_percpu_t dcpu;
1394 		dtrace_buffer_t buf;
1395 
1396 		addr = (uintptr_t)&dstate->dtds_percpu[i];
1397 
1398 		if (mdb_vread(&dcpu, sizeof (dcpu), addr) == -1) {
1399 			mdb_warn("failed to read per-CPU dstate at %p", addr);
1400 			return (-1);
1401 		}
1402 
1403 		status->dtst_dyndrops += dcpu.dtdsc_drops;
1404 		status->dtst_dyndrops_dirty += dcpu.dtdsc_dirty_drops;
1405 		status->dtst_dyndrops_rinsing += dcpu.dtdsc_rinsing_drops;
1406 
1407 		addr = (uintptr_t)&state->dts_buffer[i];
1408 
1409 		if (mdb_vread(&buf, sizeof (buf), addr) == -1) {
1410 			mdb_warn("failed to read per-CPU buffer at %p", addr);
1411 			return (-1);
1412 		}
1413 
1414 		nerrs += buf.dtb_errors;
1415 
1416 		for (j = 0; j < state->dts_nspeculations; j++) {
1417 			dtrace_speculation_t spec;
1418 
1419 			addr = (uintptr_t)&state->dts_speculations[j];
1420 
1421 			if (mdb_vread(&spec, sizeof (spec), addr) == -1) {
1422 				mdb_warn("failed to read "
1423 				    "speculation at %p", addr);
1424 				return (-1);
1425 			}
1426 
1427 			addr = (uintptr_t)&spec.dtsp_buffer[i];
1428 
1429 			if (mdb_vread(&buf, sizeof (buf), addr) == -1) {
1430 				mdb_warn("failed to read "
1431 				    "speculative buffer at %p", addr);
1432 				return (-1);
1433 			}
1434 
1435 			status->dtst_specdrops += buf.dtb_xamot_drops;
1436 		}
1437 	}
1438 
1439 	status->dtst_specdrops_busy = state->dts_speculations_busy;
1440 	status->dtst_specdrops_unavail = state->dts_speculations_unavail;
1441 	status->dtst_errors = nerrs;
1442 
1443 	return (0);
1444 }
1445 
1446 typedef struct dtracemdb_data {
1447 	dtrace_state_t *dtmd_state;
1448 	char *dtmd_symstr;
1449 	char *dtmd_modstr;
1450 	uintptr_t dtmd_addr;
1451 } dtracemdb_data_t;
1452 
1453 static int
1454 dtracemdb_ioctl(void *varg, int cmd, void *arg)
1455 {
1456 	dtracemdb_data_t *data = varg;
1457 	dtrace_state_t *state = data->dtmd_state;
1458 
1459 	switch (cmd) {
1460 	case DTRACEIOC_CONF: {
1461 		dtrace_conf_t *conf = arg;
1462 
1463 		bzero(conf, sizeof (conf));
1464 		conf->dtc_difversion = DIF_VERSION;
1465 		conf->dtc_difintregs = DIF_DIR_NREGS;
1466 		conf->dtc_diftupregs = DIF_DTR_NREGS;
1467 		conf->dtc_ctfmodel = CTF_MODEL_NATIVE;
1468 
1469 		return (0);
1470 	}
1471 
1472 	case DTRACEIOC_DOFGET: {
1473 		dof_hdr_t *hdr = arg, *dof;
1474 
1475 		dof = dtracemdb_dof_create(state);
1476 		bcopy(dof, hdr, MIN(hdr->dofh_loadsz, dof->dofh_loadsz));
1477 		mdb_free(dof, dof->dofh_loadsz);
1478 
1479 		return (0);
1480 	}
1481 
1482 	case DTRACEIOC_BUFSNAP:
1483 		return (dtracemdb_bufsnap(state->dts_buffer, arg));
1484 
1485 	case DTRACEIOC_AGGSNAP:
1486 		return (dtracemdb_bufsnap(state->dts_aggbuffer, arg));
1487 
1488 	case DTRACEIOC_AGGDESC:
1489 		return (dtracemdb_aggdesc(state, arg));
1490 
1491 	case DTRACEIOC_EPROBE:
1492 		return (dtracemdb_eprobe(state, arg));
1493 
1494 	case DTRACEIOC_PROBES:
1495 		return (dtracemdb_probe(state, arg));
1496 
1497 	case DTRACEIOC_FORMAT:
1498 		return (dtracemdb_format(state, arg));
1499 
1500 	case DTRACEIOC_STATUS:
1501 		return (dtracemdb_status(state, arg));
1502 
1503 	case DTRACEIOC_GO:
1504 		*(processorid_t *)arg = -1;
1505 		return (0);
1506 
1507 	case DTRACEIOC_ENABLE:
1508 		errno = ENOTTY; /* see dt_open.c:dtrace_go() */
1509 		return (-1);
1510 
1511 	case DTRACEIOC_PROVIDER:
1512 	case DTRACEIOC_PROBEMATCH:
1513 		errno = ESRCH;
1514 		return (-1);
1515 
1516 	default:
1517 		mdb_warn("unexpected ioctl 0x%x (%s)\n", cmd,
1518 		    cmd == DTRACEIOC_PROVIDER	? "DTRACEIOC_PROVIDER" :
1519 		    cmd == DTRACEIOC_PROBES	? "DTRACEIOC_PROBES" :
1520 		    cmd == DTRACEIOC_BUFSNAP	? "DTRACEIOC_BUFSNAP" :
1521 		    cmd == DTRACEIOC_PROBEMATCH	? "DTRACEIOC_PROBEMATCH" :
1522 		    cmd == DTRACEIOC_ENABLE	? "DTRACEIOC_ENABLE" :
1523 		    cmd == DTRACEIOC_AGGSNAP	? "DTRACEIOC_AGGSNAP" :
1524 		    cmd == DTRACEIOC_EPROBE	? "DTRACEIOC_EPROBE" :
1525 		    cmd == DTRACEIOC_PROBEARG	? "DTRACEIOC_PROBEARG" :
1526 		    cmd == DTRACEIOC_CONF	? "DTRACEIOC_CONF" :
1527 		    cmd == DTRACEIOC_STATUS	? "DTRACEIOC_STATUS" :
1528 		    cmd == DTRACEIOC_GO		? "DTRACEIOC_GO" :
1529 		    cmd == DTRACEIOC_STOP	? "DTRACEIOC_STOP" :
1530 		    cmd == DTRACEIOC_AGGDESC	? "DTRACEIOC_AGGDESC" :
1531 		    cmd == DTRACEIOC_FORMAT	? "DTRACEIOC_FORMAT" :
1532 		    cmd == DTRACEIOC_DOFGET	? "DTRACEIOC_DOFGET" :
1533 		    cmd == DTRACEIOC_REPLICATE	? "DTRACEIOC_REPLICATE" :
1534 		    "???");
1535 		errno = ENXIO;
1536 		return (-1);
1537 	}
1538 }
1539 
1540 static int
1541 dtracemdb_modctl(uintptr_t addr, const struct modctl *m, dtracemdb_data_t *data)
1542 {
1543 	struct module mod;
1544 
1545 	if (m->mod_mp == NULL)
1546 		return (WALK_NEXT);
1547 
1548 	if (mdb_vread(&mod, sizeof (mod), (uintptr_t)m->mod_mp) == -1) {
1549 		mdb_warn("couldn't read modctl %p's module", addr);
1550 		return (WALK_NEXT);
1551 	}
1552 
1553 	if ((uintptr_t)mod.text > data->dtmd_addr)
1554 		return (WALK_NEXT);
1555 
1556 	if ((uintptr_t)mod.text + mod.text_size <= data->dtmd_addr)
1557 		return (WALK_NEXT);
1558 
1559 	if (mdb_readstr(data->dtmd_modstr, MDB_SYM_NAMLEN,
1560 	    (uintptr_t)m->mod_modname) == -1)
1561 		return (WALK_ERR);
1562 
1563 	return (WALK_DONE);
1564 }
1565 
1566 static int
1567 dtracemdb_lookup_by_addr(void *varg, GElf_Addr addr, GElf_Sym *symp,
1568     dtrace_syminfo_t *sip)
1569 {
1570 	dtracemdb_data_t *data = varg;
1571 
1572 	if (data->dtmd_symstr == NULL) {
1573 		data->dtmd_symstr = mdb_zalloc(MDB_SYM_NAMLEN,
1574 		    UM_SLEEP | UM_GC);
1575 	}
1576 
1577 	if (data->dtmd_modstr == NULL) {
1578 		data->dtmd_modstr = mdb_zalloc(MDB_SYM_NAMLEN,
1579 		    UM_SLEEP | UM_GC);
1580 	}
1581 
1582 	if (symp != NULL) {
1583 		if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY, data->dtmd_symstr,
1584 		    MDB_SYM_NAMLEN, symp) == -1)
1585 			return (-1);
1586 	}
1587 
1588 	if (sip != NULL) {
1589 		data->dtmd_addr = addr;
1590 
1591 		(void) strcpy(data->dtmd_modstr, "???");
1592 
1593 		if (mdb_walk("modctl",
1594 		    (mdb_walk_cb_t)dtracemdb_modctl, varg) == -1) {
1595 			mdb_warn("couldn't walk 'modctl'");
1596 			return (-1);
1597 		}
1598 
1599 		sip->dts_object = data->dtmd_modstr;
1600 		sip->dts_id = 0;
1601 		sip->dts_name = symp != NULL ? data->dtmd_symstr : NULL;
1602 	}
1603 
1604 	return (0);
1605 }
1606 
1607 /*ARGSUSED*/
1608 static int
1609 dtracemdb_stat(void *varg, processorid_t cpu)
1610 {
1611 	GElf_Sym sym;
1612 	cpu_t c;
1613 	uintptr_t caddr, addr;
1614 
1615 	if (mdb_lookup_by_name("cpu", &sym) == -1) {
1616 		mdb_warn("failed to find symbol for 'cpu'");
1617 		return (-1);
1618 	}
1619 
1620 	if (cpu * sizeof (uintptr_t) > sym.st_size)
1621 		return (-1);
1622 
1623 	addr = (uintptr_t)sym.st_value + cpu * sizeof (uintptr_t);
1624 
1625 	if (mdb_vread(&caddr, sizeof (caddr), addr) == -1) {
1626 		mdb_warn("failed to read cpu[%d]", cpu);
1627 		return (-1);
1628 	}
1629 
1630 	if (caddr == NULL)
1631 		return (-1);
1632 
1633 	if (mdb_vread(&c, sizeof (c), caddr) == -1) {
1634 		mdb_warn("failed to read cpu at %p", caddr);
1635 		return (-1);
1636 	}
1637 
1638 	if (c.cpu_flags & CPU_POWEROFF) {
1639 		return (P_POWEROFF);
1640 	} else if (c.cpu_flags & CPU_SPARE) {
1641 		return (P_SPARE);
1642 	} else if (c.cpu_flags & CPU_FAULTED) {
1643 		return (P_FAULTED);
1644 	} else if ((c.cpu_flags & (CPU_READY | CPU_OFFLINE)) != CPU_READY) {
1645 		return (P_OFFLINE);
1646 	} else if (c.cpu_flags & CPU_ENABLE) {
1647 		return (P_ONLINE);
1648 	} else {
1649 		return (P_NOINTR);
1650 	}
1651 }
1652 
1653 /*ARGSUSED*/
1654 static long
1655 dtracemdb_sysconf(void *varg, int name)
1656 {
1657 	int max_ncpus;
1658 	processorid_t max_cpuid;
1659 
1660 	switch (name) {
1661 	case _SC_CPUID_MAX:
1662 		if (mdb_readvar(&max_cpuid, "max_cpuid") == -1) {
1663 			mdb_warn("failed to read 'max_cpuid'");
1664 			return (-1);
1665 		}
1666 
1667 		return (max_cpuid);
1668 
1669 	case _SC_NPROCESSORS_MAX:
1670 		if (mdb_readvar(&max_ncpus, "max_ncpus") == -1) {
1671 			mdb_warn("failed to read 'max_ncpus'");
1672 			return (-1);
1673 		}
1674 
1675 		return (max_ncpus);
1676 
1677 	default:
1678 		mdb_warn("unexpected sysconf code %d\n", name);
1679 		return (-1);
1680 	}
1681 }
1682 
1683 const dtrace_vector_t dtrace_mdbops = {
1684 	dtracemdb_ioctl,
1685 	dtracemdb_lookup_by_addr,
1686 	dtracemdb_stat,
1687 	dtracemdb_sysconf
1688 };
1689 
1690 typedef struct dtrace_dcmddata {
1691 	dtrace_hdl_t *dtdd_dtp;
1692 	int dtdd_cpu;
1693 	int dtdd_quiet;
1694 	int dtdd_flowindent;
1695 	int dtdd_heading;
1696 } dtrace_dcmddata_t;
1697 
1698 /*ARGSUSED*/
1699 static int
1700 dtrace_dcmdrec(const dtrace_probedata_t *data,
1701     const dtrace_recdesc_t *rec, void *arg)
1702 {
1703 	dtrace_dcmddata_t *dd = arg;
1704 
1705 	if (rec == NULL) {
1706 		/*
1707 		 * We have processed the final record; output the newline if
1708 		 * we're not in quiet mode.
1709 		 */
1710 		if (!dd->dtdd_quiet)
1711 			mdb_printf("\n");
1712 
1713 		return (DTRACE_CONSUME_NEXT);
1714 	}
1715 
1716 	return (DTRACE_CONSUME_THIS);
1717 }
1718 
1719 /*ARGSUSED*/
1720 static int
1721 dtrace_dcmdprobe(const dtrace_probedata_t *data, void *arg)
1722 {
1723 	dtrace_probedesc_t *pd = data->dtpda_pdesc;
1724 	processorid_t cpu = data->dtpda_cpu;
1725 	dtrace_dcmddata_t *dd = arg;
1726 	char name[DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 2];
1727 
1728 	if (dd->dtdd_cpu != -1UL && dd->dtdd_cpu != cpu)
1729 		return (DTRACE_CONSUME_NEXT);
1730 
1731 	if (dd->dtdd_heading == 0) {
1732 		if (!dd->dtdd_flowindent) {
1733 			if (!dd->dtdd_quiet) {
1734 				mdb_printf("%3s %6s %32s\n",
1735 				    "CPU", "ID", "FUNCTION:NAME");
1736 			}
1737 		} else {
1738 			mdb_printf("%3s %-41s\n", "CPU", "FUNCTION");
1739 		}
1740 		dd->dtdd_heading = 1;
1741 	}
1742 
1743 	if (!dd->dtdd_flowindent) {
1744 		if (!dd->dtdd_quiet) {
1745 			(void) mdb_snprintf(name, sizeof (name), "%s:%s",
1746 			    pd->dtpd_func, pd->dtpd_name);
1747 
1748 			mdb_printf("%3d %6d %32s ", cpu, pd->dtpd_id, name);
1749 		}
1750 	} else {
1751 		int indent = data->dtpda_indent;
1752 
1753 		if (data->dtpda_flow == DTRACEFLOW_NONE) {
1754 			(void) mdb_snprintf(name, sizeof (name), "%*s%s%s:%s",
1755 			    indent, "", data->dtpda_prefix, pd->dtpd_func,
1756 			    pd->dtpd_name);
1757 		} else {
1758 			(void) mdb_snprintf(name, sizeof (name), "%*s%s%s",
1759 			    indent, "", data->dtpda_prefix, pd->dtpd_func);
1760 		}
1761 
1762 		mdb_printf("%3d %-41s ", cpu, name);
1763 	}
1764 
1765 	return (DTRACE_CONSUME_THIS);
1766 }
1767 
1768 /*ARGSUSED*/
1769 static int
1770 dtrace_dcmderr(dtrace_errdata_t *data, void *arg)
1771 {
1772 	mdb_warn(data->dteda_msg);
1773 	return (DTRACE_HANDLE_OK);
1774 }
1775 
1776 /*ARGSUSED*/
1777 static int
1778 dtrace_dcmddrop(dtrace_dropdata_t *data, void *arg)
1779 {
1780 	mdb_warn(data->dtdda_msg);
1781 	return (DTRACE_HANDLE_OK);
1782 }
1783 
1784 /*ARGSUSED*/
1785 static int
1786 dtrace_dcmdbuffered(dtrace_bufdata_t *bufdata, void *arg)
1787 {
1788 	mdb_printf("%s", bufdata->dtbda_buffered);
1789 	return (DTRACE_HANDLE_OK);
1790 }
1791 
1792 /*ARGSUSED*/
1793 int
1794 dtrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1795 {
1796 	dtrace_state_t state;
1797 	dtrace_hdl_t *dtp;
1798 	int ncpu, err;
1799 	uintptr_t c = -1UL;
1800 	dtrace_dcmddata_t dd;
1801 	dtrace_optval_t val;
1802 	dtracemdb_data_t md;
1803 	int rval = DCMD_ERR;
1804 
1805 	if (!(flags & DCMD_ADDRSPEC))
1806 		return (DCMD_USAGE);
1807 
1808 	if (mdb_getopts(argc, argv, 'c', MDB_OPT_UINTPTR, &c, NULL) != argc)
1809 		return (DCMD_USAGE);
1810 
1811 	if (mdb_readvar(&ncpu, "_ncpu") == -1) {
1812 		mdb_warn("failed to read '_ncpu'");
1813 		return (DCMD_ERR);
1814 	}
1815 
1816 	if (mdb_vread(&state, sizeof (state), addr) == -1) {
1817 		mdb_warn("couldn't read dtrace_state_t at %p", addr);
1818 		return (DCMD_ERR);
1819 	}
1820 
1821 	bzero(&md, sizeof (md));
1822 	md.dtmd_state = &state;
1823 
1824 	if ((dtp = dtrace_vopen(DTRACE_VERSION, DTRACE_O_NOSYS, &err,
1825 	    &dtrace_mdbops, &md)) == NULL) {
1826 		mdb_warn("failed to initialize dtrace: %s\n",
1827 		    dtrace_errmsg(NULL, err));
1828 		return (DCMD_ERR);
1829 	}
1830 
1831 	if (dtrace_go(dtp) != 0) {
1832 		mdb_warn("failed to initialize dtrace: %s\n",
1833 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1834 		goto err;
1835 	}
1836 
1837 	bzero(&dd, sizeof (dd));
1838 	dd.dtdd_dtp = dtp;
1839 	dd.dtdd_cpu = c;
1840 
1841 	if (dtrace_getopt(dtp, "flowindent", &val) == -1) {
1842 		mdb_warn("couldn't get 'flowindent' option: %s\n",
1843 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1844 		goto err;
1845 	}
1846 
1847 	dd.dtdd_flowindent = (val != DTRACEOPT_UNSET);
1848 
1849 	if (dtrace_getopt(dtp, "quiet", &val) == -1) {
1850 		mdb_warn("couldn't get 'quiet' option: %s\n",
1851 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1852 		goto err;
1853 	}
1854 
1855 	dd.dtdd_quiet = (val != DTRACEOPT_UNSET);
1856 
1857 	if (dtrace_handle_err(dtp, dtrace_dcmderr, NULL) == -1) {
1858 		mdb_warn("couldn't add err handler: %s\n",
1859 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1860 		goto err;
1861 	}
1862 
1863 	if (dtrace_handle_drop(dtp, dtrace_dcmddrop, NULL) == -1) {
1864 		mdb_warn("couldn't add drop handler: %s\n",
1865 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1866 		goto err;
1867 	}
1868 
1869 	if (dtrace_handle_buffered(dtp, dtrace_dcmdbuffered, NULL) == -1) {
1870 		mdb_warn("couldn't add buffered handler: %s\n",
1871 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1872 		goto err;
1873 	}
1874 
1875 	if (dtrace_status(dtp) == -1) {
1876 		mdb_warn("couldn't get status: %s\n",
1877 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1878 		goto err;
1879 	}
1880 
1881 	if (dtrace_aggregate_snap(dtp) == -1) {
1882 		mdb_warn("couldn't snapshot aggregation: %s\n",
1883 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1884 		goto err;
1885 	}
1886 
1887 	if (dtrace_consume(dtp, NULL,
1888 	    dtrace_dcmdprobe, dtrace_dcmdrec, &dd) == -1) {
1889 		mdb_warn("couldn't consume DTrace buffers: %s\n",
1890 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1891 	}
1892 
1893 	if (dtrace_aggregate_print(dtp, NULL, NULL) == -1) {
1894 		mdb_warn("couldn't print aggregation: %s\n",
1895 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1896 		goto err;
1897 	}
1898 
1899 	rval = DCMD_OK;
1900 err:
1901 	dtrace_close(dtp);
1902 	return (rval);
1903 }
1904 
1905 static int
1906 dtrace_errhash_cmp(const void *l, const void *r)
1907 {
1908 	uintptr_t lhs = *((uintptr_t *)l);
1909 	uintptr_t rhs = *((uintptr_t *)r);
1910 	dtrace_errhash_t lerr, rerr;
1911 	char lmsg[256], rmsg[256];
1912 
1913 	(void) mdb_vread(&lerr, sizeof (lerr), lhs);
1914 	(void) mdb_vread(&rerr, sizeof (rerr), rhs);
1915 
1916 	if (lerr.dter_msg == NULL)
1917 		return (-1);
1918 
1919 	if (rerr.dter_msg == NULL)
1920 		return (1);
1921 
1922 	(void) mdb_readstr(lmsg, sizeof (lmsg), (uintptr_t)lerr.dter_msg);
1923 	(void) mdb_readstr(rmsg, sizeof (rmsg), (uintptr_t)rerr.dter_msg);
1924 
1925 	return (strcmp(lmsg, rmsg));
1926 }
1927 
1928 int
1929 dtrace_errhash_init(mdb_walk_state_t *wsp)
1930 {
1931 	GElf_Sym sym;
1932 	uintptr_t *hash, addr;
1933 	int i;
1934 
1935 	if (wsp->walk_addr != NULL) {
1936 		mdb_warn("dtrace_errhash walk only supports global walks\n");
1937 		return (WALK_ERR);
1938 	}
1939 
1940 	if (mdb_lookup_by_name("dtrace_errhash", &sym) == -1) {
1941 		mdb_warn("couldn't find 'dtrace_errhash' (non-DEBUG kernel?)");
1942 		return (WALK_ERR);
1943 	}
1944 
1945 	addr = (uintptr_t)sym.st_value;
1946 	hash = mdb_alloc(DTRACE_ERRHASHSZ * sizeof (uintptr_t),
1947 	    UM_SLEEP | UM_GC);
1948 
1949 	for (i = 0; i < DTRACE_ERRHASHSZ; i++)
1950 		hash[i] = addr + i * sizeof (dtrace_errhash_t);
1951 
1952 	qsort(hash, DTRACE_ERRHASHSZ, sizeof (uintptr_t), dtrace_errhash_cmp);
1953 
1954 	wsp->walk_addr = 0;
1955 	wsp->walk_data = hash;
1956 
1957 	return (WALK_NEXT);
1958 }
1959 
1960 int
1961 dtrace_errhash_step(mdb_walk_state_t *wsp)
1962 {
1963 	int ndx = (int)wsp->walk_addr;
1964 	uintptr_t *hash = wsp->walk_data;
1965 	dtrace_errhash_t err;
1966 	uintptr_t addr;
1967 
1968 	if (ndx >= DTRACE_ERRHASHSZ)
1969 		return (WALK_DONE);
1970 
1971 	wsp->walk_addr = ndx + 1;
1972 	addr = hash[ndx];
1973 
1974 	if (mdb_vread(&err, sizeof (err), addr) == -1) {
1975 		mdb_warn("failed to read dtrace_errhash_t at %p", addr);
1976 		return (WALK_DONE);
1977 	}
1978 
1979 	if (err.dter_msg == NULL)
1980 		return (WALK_NEXT);
1981 
1982 	return (wsp->walk_callback(addr, &err, wsp->walk_cbdata));
1983 }
1984 
1985 /*ARGSUSED*/
1986 int
1987 dtrace_errhash(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1988 {
1989 	dtrace_errhash_t err;
1990 	char msg[256];
1991 
1992 	if (!(flags & DCMD_ADDRSPEC)) {
1993 		if (mdb_walk_dcmd("dtrace_errhash", "dtrace_errhash",
1994 		    argc, argv) == -1) {
1995 			mdb_warn("can't walk 'dtrace_errhash'");
1996 			return (DCMD_ERR);
1997 		}
1998 
1999 		return (DCMD_OK);
2000 	}
2001 
2002 	if (DCMD_HDRSPEC(flags))
2003 		mdb_printf("%8s %s\n", "COUNT", "ERROR");
2004 
2005 	if (mdb_vread(&err, sizeof (err), addr) == -1) {
2006 		mdb_warn("failed to read dtrace_errhash_t at %p", addr);
2007 		return (DCMD_ERR);
2008 	}
2009 
2010 	addr = (uintptr_t)err.dter_msg;
2011 
2012 	if (mdb_readstr(msg, sizeof (msg), addr) == -1) {
2013 		mdb_warn("failed to read error msg at %p", addr);
2014 		return (DCMD_ERR);
2015 	}
2016 
2017 	mdb_printf("%8d %s", err.dter_count, msg);
2018 
2019 	/*
2020 	 * Some error messages include a newline -- only print the newline
2021 	 * if the message doesn't have one.
2022 	 */
2023 	if (msg[strlen(msg) - 1] != '\n')
2024 		mdb_printf("\n");
2025 
2026 	return (DCMD_OK);
2027 }
2028 
2029 int
2030 dtrace_helptrace_init(mdb_walk_state_t *wsp)
2031 {
2032 	uint32_t next;
2033 	int enabled;
2034 
2035 	if (wsp->walk_addr != NULL) {
2036 		mdb_warn("dtrace_helptrace only supports global walks\n");
2037 		return (WALK_ERR);
2038 	}
2039 
2040 	if (mdb_readvar(&enabled, "dtrace_helptrace_enabled") == -1) {
2041 		mdb_warn("couldn't read 'dtrace_helptrace_enabled'");
2042 		return (WALK_ERR);
2043 	}
2044 
2045 	if (!enabled) {
2046 		mdb_warn("helper tracing is not enabled\n");
2047 		return (WALK_ERR);
2048 	}
2049 
2050 	if (mdb_readvar(&next, "dtrace_helptrace_next") == -1) {
2051 		mdb_warn("couldn't read 'dtrace_helptrace_next'");
2052 		return (WALK_ERR);
2053 	}
2054 
2055 	wsp->walk_addr = next;
2056 
2057 	return (WALK_NEXT);
2058 }
2059 
2060 int
2061 dtrace_helptrace_step(mdb_walk_state_t *wsp)
2062 {
2063 	uint32_t next, size, nlocals, bufsize;
2064 	uintptr_t buffer, addr;
2065 	dtrace_helptrace_t *ht;
2066 	int rval;
2067 
2068 	if (mdb_readvar(&next, "dtrace_helptrace_next") == -1) {
2069 		mdb_warn("couldn't read 'dtrace_helptrace_next'");
2070 		return (WALK_ERR);
2071 	}
2072 
2073 	if (mdb_readvar(&bufsize, "dtrace_helptrace_bufsize") == -1) {
2074 		mdb_warn("couldn't read 'dtrace_helptrace_bufsize'");
2075 		return (WALK_ERR);
2076 	}
2077 
2078 	if (mdb_readvar(&buffer, "dtrace_helptrace_buffer") == -1) {
2079 		mdb_warn("couldn't read 'dtrace_helptrace_buffer'");
2080 		return (WALK_ERR);
2081 	}
2082 
2083 	if (mdb_readvar(&nlocals, "dtrace_helptrace_nlocals") == -1) {
2084 		mdb_warn("couldn't read 'dtrace_helptrace_nlocals'");
2085 		return (WALK_ERR);
2086 	}
2087 
2088 	size = sizeof (dtrace_helptrace_t) +
2089 	    nlocals * sizeof (uint64_t) - sizeof (uint64_t);
2090 
2091 	if (wsp->walk_addr + size > bufsize) {
2092 		if (next == 0)
2093 			return (WALK_DONE);
2094 
2095 		wsp->walk_addr = 0;
2096 	}
2097 
2098 	addr = buffer + wsp->walk_addr;
2099 	ht = alloca(size);
2100 
2101 	if (mdb_vread(ht, size, addr) == -1) {
2102 		mdb_warn("couldn't read entry at %p", addr);
2103 		return (WALK_ERR);
2104 	}
2105 
2106 	if (ht->dtht_helper != NULL) {
2107 		rval = wsp->walk_callback(addr, ht, wsp->walk_cbdata);
2108 
2109 		if (rval != WALK_NEXT)
2110 			return (rval);
2111 	}
2112 
2113 	if (wsp->walk_addr < next && wsp->walk_addr + size >= next)
2114 		return (WALK_DONE);
2115 
2116 	wsp->walk_addr += size;
2117 	return (WALK_NEXT);
2118 }
2119 
2120 int
2121 dtrace_helptrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2122 {
2123 	dtrace_helptrace_t help;
2124 	dtrace_helper_action_t helper;
2125 	char where[30];
2126 	uint_t opt_v = FALSE;
2127 	uintptr_t haddr;
2128 
2129 	if (!(flags & DCMD_ADDRSPEC)) {
2130 		if (mdb_walk_dcmd("dtrace_helptrace", "dtrace_helptrace",
2131 		    argc, argv) == -1) {
2132 			mdb_warn("can't walk 'dtrace_helptrace'");
2133 			return (DCMD_ERR);
2134 		}
2135 
2136 		return (DCMD_OK);
2137 	}
2138 
2139 	if (mdb_getopts(argc, argv, 'v',
2140 	    MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
2141 		return (DCMD_USAGE);
2142 
2143 	if (DCMD_HDRSPEC(flags)) {
2144 		mdb_printf(" %?s %?s %12s %s\n",
2145 		    "ADDR", "HELPER", "WHERE", "DIFO");
2146 	}
2147 
2148 	if (mdb_vread(&help, sizeof (help), addr) == -1) {
2149 		mdb_warn("failed to read dtrace_helptrace_t at %p", addr);
2150 		return (DCMD_ERR);
2151 	}
2152 
2153 	switch (help.dtht_where) {
2154 	case 0:
2155 		(void) mdb_snprintf(where, sizeof (where), "predicate");
2156 		break;
2157 
2158 	case DTRACE_HELPTRACE_NEXT:
2159 		(void) mdb_snprintf(where, sizeof (where), "next");
2160 		break;
2161 
2162 	case DTRACE_HELPTRACE_DONE:
2163 		(void) mdb_snprintf(where, sizeof (where), "done");
2164 		break;
2165 
2166 	case DTRACE_HELPTRACE_ERR:
2167 		(void) mdb_snprintf(where, sizeof (where), "err");
2168 		break;
2169 
2170 	default:
2171 		(void) mdb_snprintf(where, sizeof (where),
2172 		    "action #%d", help.dtht_where);
2173 		break;
2174 	}
2175 
2176 	mdb_printf(" %?p %?p %12s ", addr, help.dtht_helper, where);
2177 
2178 	haddr = (uintptr_t)help.dtht_helper;
2179 
2180 	if (mdb_vread(&helper, sizeof (helper), haddr) == -1) {
2181 		/*
2182 		 * We're not going to warn in this case -- we're just not going
2183 		 * to print anything exciting.
2184 		 */
2185 		mdb_printf("???\n");
2186 	} else {
2187 		switch (help.dtht_where) {
2188 		case 0:
2189 			mdb_printf("%p\n", helper.dthp_predicate);
2190 			break;
2191 
2192 		case DTRACE_HELPTRACE_NEXT:
2193 		case DTRACE_HELPTRACE_DONE:
2194 		case DTRACE_HELPTRACE_ERR:
2195 			mdb_printf("-\n");
2196 			break;
2197 
2198 		default:
2199 			haddr = (uintptr_t)helper.dthp_actions +
2200 			    (help.dtht_where - 1) * sizeof (uintptr_t);
2201 
2202 			if (mdb_vread(&haddr, sizeof (haddr), haddr) == -1) {
2203 				mdb_printf("???\n");
2204 			} else {
2205 				mdb_printf("%p\n", haddr);
2206 			}
2207 		}
2208 	}
2209 
2210 	if (opt_v) {
2211 		int i;
2212 
2213 		mdb_printf("%?s|\n%?s+--> %?s %4s %s\n", "", "",
2214 		    "ADDR", "NDX", "VALUE");
2215 		addr += sizeof (help) - sizeof (uint64_t);
2216 
2217 		for (i = 0; i < help.dtht_nlocals; i++) {
2218 			uint64_t val;
2219 
2220 			if (mdb_vread(&val, sizeof (val), addr) == -1) {
2221 				mdb_warn("couldn't read local at %p", addr);
2222 				continue;
2223 			}
2224 
2225 			mdb_printf("%?s     %?p %4d %p\n", "", addr, i, val);
2226 			addr += sizeof (uint64_t);
2227 		}
2228 
2229 		mdb_printf("\n");
2230 	}
2231 
2232 	return (DCMD_OK);
2233 }
2234 
2235 /*ARGSUSED*/
2236 static int
2237 dtrace_state_walk(uintptr_t addr, const vmem_seg_t *seg, minor_t *highest)
2238 {
2239 	if (seg->vs_end > *highest)
2240 		*highest = seg->vs_end;
2241 
2242 	return (WALK_NEXT);
2243 }
2244 
2245 typedef struct dtrace_state_walk {
2246 	uintptr_t dtsw_softstate;
2247 	minor_t dtsw_max;
2248 	minor_t dtsw_current;
2249 } dtrace_state_walk_t;
2250 
2251 int
2252 dtrace_state_init(mdb_walk_state_t *wsp)
2253 {
2254 	uintptr_t dtrace_minor;
2255 	minor_t max = 0;
2256 	dtrace_state_walk_t *dw;
2257 
2258 	if (wsp->walk_addr != NULL) {
2259 		mdb_warn("dtrace_state only supports global walks\n");
2260 		return (WALK_ERR);
2261 	}
2262 
2263 	/*
2264 	 * Find the dtrace_minor vmem arena and walk it to get the maximum
2265 	 * minor number.
2266 	 */
2267 	if (mdb_readvar(&dtrace_minor, "dtrace_minor") == -1) {
2268 		mdb_warn("failed to read 'dtrace_minor'");
2269 		return (WALK_ERR);
2270 	}
2271 
2272 	if (mdb_pwalk("vmem_alloc", (mdb_walk_cb_t)dtrace_state_walk,
2273 	    &max, dtrace_minor) == -1) {
2274 		mdb_warn("couldn't walk 'vmem_alloc'");
2275 		return (WALK_ERR);
2276 	}
2277 
2278 	dw = mdb_zalloc(sizeof (dtrace_state_walk_t), UM_SLEEP | UM_GC);
2279 	dw->dtsw_current = 0;
2280 	dw->dtsw_max = max;
2281 
2282 	if (mdb_readvar(&dw->dtsw_softstate, "dtrace_softstate") == -1) {
2283 		mdb_warn("failed to read 'dtrace_softstate'");
2284 		return (DCMD_ERR);
2285 	}
2286 
2287 	wsp->walk_data = dw;
2288 
2289 	return (WALK_NEXT);
2290 }
2291 
2292 int
2293 dtrace_state_step(mdb_walk_state_t *wsp)
2294 {
2295 	dtrace_state_walk_t *dw = wsp->walk_data;
2296 	uintptr_t statep;
2297 	dtrace_state_t state;
2298 	int rval;
2299 
2300 	while (mdb_get_soft_state_byaddr(dw->dtsw_softstate, dw->dtsw_current,
2301 	    &statep, NULL, 0) == -1) {
2302 		if (dw->dtsw_current >= dw->dtsw_max)
2303 			return (WALK_DONE);
2304 
2305 		dw->dtsw_current++;
2306 	}
2307 
2308 	if (mdb_vread(&state, sizeof (state), statep) == -1) {
2309 		mdb_warn("couldn't read dtrace_state_t at %p", statep);
2310 		return (WALK_NEXT);
2311 	}
2312 
2313 	rval = wsp->walk_callback(statep, &state, wsp->walk_cbdata);
2314 	dw->dtsw_current++;
2315 
2316 	return (rval);
2317 }
2318 
2319 typedef struct dtrace_state_data {
2320 	int dtsd_major;
2321 	uintptr_t dtsd_proc;
2322 	uintptr_t dtsd_softstate;
2323 	uintptr_t dtsd_state;
2324 } dtrace_state_data_t;
2325 
2326 static int
2327 dtrace_state_file(uintptr_t addr, struct file *f, dtrace_state_data_t *data)
2328 {
2329 	vnode_t vnode;
2330 	proc_t proc;
2331 	minor_t minor;
2332 	uintptr_t statep;
2333 
2334 	if (mdb_vread(&vnode, sizeof (vnode), (uintptr_t)f->f_vnode) == -1) {
2335 		mdb_warn("couldn't read vnode at %p", (uintptr_t)f->f_vnode);
2336 		return (WALK_NEXT);
2337 	}
2338 
2339 	if (getmajor(vnode.v_rdev) != data->dtsd_major)
2340 		return (WALK_NEXT);
2341 
2342 	minor = getminor(vnode.v_rdev);
2343 
2344 	if (mdb_vread(&proc, sizeof (proc), data->dtsd_proc) == -1) {
2345 		mdb_warn("failed to read proc at %p", data->dtsd_proc);
2346 		return (WALK_NEXT);
2347 	}
2348 
2349 	if (mdb_get_soft_state_byaddr(data->dtsd_softstate, minor,
2350 	    &statep, NULL, 0) == -1) {
2351 		mdb_warn("failed to read softstate for minor %d", minor);
2352 		return (WALK_NEXT);
2353 	}
2354 
2355 	if (statep != data->dtsd_state)
2356 		return (WALK_NEXT);
2357 
2358 	mdb_printf("%?p %5d %?p %-*s %?p\n", statep, minor,
2359 	    data->dtsd_proc, MAXCOMLEN, proc.p_user.u_comm, addr);
2360 
2361 	return (WALK_NEXT);
2362 }
2363 
2364 /*ARGSUSED*/
2365 static int
2366 dtrace_state_proc(uintptr_t addr, void *ignored, dtrace_state_data_t *data)
2367 {
2368 	data->dtsd_proc = addr;
2369 
2370 	if (mdb_pwalk("file",
2371 	    (mdb_walk_cb_t)dtrace_state_file, data, addr) == -1) {
2372 		mdb_warn("couldn't walk 'file' for proc %p", addr);
2373 		return (WALK_ERR);
2374 	}
2375 
2376 	return (WALK_NEXT);
2377 }
2378 
2379 void
2380 dtrace_state_help(void)
2381 {
2382 	mdb_printf("Given a dtrace_state_t structure, displays all "
2383 	    /*CSTYLED*/
2384 	    "consumers, or \"<anonymous>\"\nif the consumer is anonymous.  If "
2385 	    "no state structure is provided, iterates\nover all state "
2386 	    "structures.\n\n"
2387 	    "Addresses in ADDR column may be provided to ::dtrace to obtain\n"
2388 	    "dtrace(1M)-like output for in-kernel DTrace data.\n");
2389 }
2390 
2391 int
2392 dtrace_state(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2393 {
2394 	uintptr_t devi;
2395 	struct dev_info info;
2396 	dtrace_state_data_t data;
2397 	dtrace_anon_t anon;
2398 	dtrace_state_t state;
2399 
2400 	if (!(flags & DCMD_ADDRSPEC)) {
2401 		if (mdb_walk_dcmd("dtrace_state",
2402 		    "dtrace_state", argc, argv) == -1) {
2403 			mdb_warn("can't walk dtrace_state");
2404 			return (DCMD_ERR);
2405 		}
2406 		return (DCMD_OK);
2407 	}
2408 
2409 	if (DCMD_HDRSPEC(flags)) {
2410 		mdb_printf("%?s %5s %?s %-*s %?s\n", "ADDR", "MINOR", "PROC",
2411 		    MAXCOMLEN, "NAME", "FILE");
2412 	}
2413 
2414 	/*
2415 	 * First determine if this is anonymous state.
2416 	 */
2417 	if (mdb_readvar(&anon, "dtrace_anon") == -1) {
2418 		mdb_warn("failed to read 'dtrace_anon'");
2419 		return (DCMD_ERR);
2420 	}
2421 
2422 	if ((uintptr_t)anon.dta_state == addr) {
2423 		if (mdb_vread(&state, sizeof (state), addr) == -1) {
2424 			mdb_warn("failed to read anon at %p", addr);
2425 			return (DCMD_ERR);
2426 		}
2427 
2428 		mdb_printf("%?p %5d %?s %-*s %?s\n", addr,
2429 		    getminor(state.dts_dev), "-", MAXCOMLEN,
2430 		    "<anonymous>", "-");
2431 
2432 		return (DCMD_OK);
2433 	}
2434 
2435 	if (mdb_readvar(&devi, "dtrace_devi") == -1) {
2436 		mdb_warn("failed to read 'dtrace_devi'");
2437 		return (DCMD_ERR);
2438 	}
2439 
2440 	if (mdb_vread(&info, sizeof (struct dev_info), devi) == -1) {
2441 		mdb_warn("failed to read 'dev_info'");
2442 		return (DCMD_ERR);
2443 	}
2444 
2445 	data.dtsd_major = info.devi_major;
2446 
2447 	if (mdb_readvar(&data.dtsd_softstate, "dtrace_softstate") == -1) {
2448 		mdb_warn("failed to read 'dtrace_softstate'");
2449 		return (DCMD_ERR);
2450 	}
2451 
2452 	data.dtsd_state = addr;
2453 
2454 	/*
2455 	 * Walk through all processes and all open files looking for this
2456 	 * state.  It must be open somewhere...
2457 	 */
2458 	if (mdb_walk("proc", (mdb_walk_cb_t)dtrace_state_proc, &data) == -1) {
2459 		mdb_warn("couldn't walk 'proc'");
2460 		return (DCMD_ERR);
2461 	}
2462 
2463 	return (DCMD_OK);
2464 }
2465 
2466 typedef struct dtrace_aggkey_data {
2467 	uintptr_t *dtakd_hash;
2468 	uintptr_t dtakd_hashsize;
2469 	uintptr_t dtakd_next;
2470 	uintptr_t dtakd_ndx;
2471 } dtrace_aggkey_data_t;
2472 
2473 int
2474 dtrace_aggkey_init(mdb_walk_state_t *wsp)
2475 {
2476 	dtrace_buffer_t buf;
2477 	uintptr_t addr;
2478 	dtrace_aggbuffer_t agb;
2479 	dtrace_aggkey_data_t *data;
2480 	size_t hsize;
2481 
2482 	if ((addr = wsp->walk_addr) == NULL) {
2483 		mdb_warn("dtrace_aggkey walk needs aggregation buffer\n");
2484 		return (WALK_ERR);
2485 	}
2486 
2487 	if (mdb_vread(&buf, sizeof (buf), addr) == -1) {
2488 		mdb_warn("failed to read aggregation buffer at %p", addr);
2489 		return (WALK_ERR);
2490 	}
2491 
2492 	addr = (uintptr_t)buf.dtb_tomax +
2493 	    buf.dtb_size - sizeof (dtrace_aggbuffer_t);
2494 
2495 	if (mdb_vread(&agb, sizeof (agb), addr) == -1) {
2496 		mdb_warn("failed to read dtrace_aggbuffer_t at %p", addr);
2497 		return (WALK_ERR);
2498 	}
2499 
2500 	data = mdb_zalloc(sizeof (dtrace_aggkey_data_t), UM_SLEEP);
2501 
2502 	data->dtakd_hashsize = agb.dtagb_hashsize;
2503 	hsize = agb.dtagb_hashsize * sizeof (dtrace_aggkey_t *);
2504 	data->dtakd_hash = mdb_alloc(hsize, UM_SLEEP);
2505 
2506 	if (mdb_vread(data->dtakd_hash, hsize,
2507 	    (uintptr_t)agb.dtagb_hash) == -1) {
2508 		mdb_warn("failed to read hash at %p",
2509 		    (uintptr_t)agb.dtagb_hash);
2510 		mdb_free(data->dtakd_hash, hsize);
2511 		mdb_free(data, sizeof (dtrace_aggkey_data_t));
2512 		return (WALK_ERR);
2513 	}
2514 
2515 	wsp->walk_data = data;
2516 	return (WALK_NEXT);
2517 }
2518 
2519 int
2520 dtrace_aggkey_step(mdb_walk_state_t *wsp)
2521 {
2522 	dtrace_aggkey_data_t *data = wsp->walk_data;
2523 	dtrace_aggkey_t key;
2524 	uintptr_t addr;
2525 
2526 	while ((addr = data->dtakd_next) == NULL) {
2527 		if (data->dtakd_ndx == data->dtakd_hashsize)
2528 			return (WALK_DONE);
2529 
2530 		data->dtakd_next = data->dtakd_hash[data->dtakd_ndx++];
2531 	}
2532 
2533 	if (mdb_vread(&key, sizeof (key), addr) == -1) {
2534 		mdb_warn("failed to read dtrace_aggkey_t at %p", addr);
2535 		return (WALK_ERR);
2536 	}
2537 
2538 	data->dtakd_next = (uintptr_t)key.dtak_next;
2539 
2540 	return (wsp->walk_callback(addr, &key, wsp->walk_cbdata));
2541 }
2542 
2543 void
2544 dtrace_aggkey_fini(mdb_walk_state_t *wsp)
2545 {
2546 	dtrace_aggkey_data_t *data = wsp->walk_data;
2547 	size_t hsize;
2548 
2549 	hsize = data->dtakd_hashsize * sizeof (dtrace_aggkey_t *);
2550 	mdb_free(data->dtakd_hash, hsize);
2551 	mdb_free(data, sizeof (dtrace_aggkey_data_t));
2552 }
2553 
2554 typedef struct dtrace_dynvar_data {
2555 	dtrace_dynhash_t *dtdvd_hash;
2556 	uintptr_t dtdvd_hashsize;
2557 	uintptr_t dtdvd_next;
2558 	uintptr_t dtdvd_ndx;
2559 } dtrace_dynvar_data_t;
2560 
2561 int
2562 dtrace_dynvar_init(mdb_walk_state_t *wsp)
2563 {
2564 	uintptr_t addr;
2565 	dtrace_dstate_t dstate;
2566 	dtrace_dynvar_data_t *data;
2567 	size_t hsize;
2568 
2569 	if ((addr = wsp->walk_addr) == NULL) {
2570 		mdb_warn("dtrace_dynvar walk needs dtrace_dstate_t\n");
2571 		return (WALK_ERR);
2572 	}
2573 
2574 	if (mdb_vread(&dstate, sizeof (dstate), addr) == -1) {
2575 		mdb_warn("failed to read dynamic state at %p", addr);
2576 		return (WALK_ERR);
2577 	}
2578 
2579 	data = mdb_zalloc(sizeof (dtrace_dynvar_data_t), UM_SLEEP);
2580 
2581 	data->dtdvd_hashsize = dstate.dtds_hashsize;
2582 	hsize = dstate.dtds_hashsize * sizeof (dtrace_dynhash_t);
2583 	data->dtdvd_hash = mdb_alloc(hsize, UM_SLEEP);
2584 
2585 	if (mdb_vread(data->dtdvd_hash, hsize,
2586 	    (uintptr_t)dstate.dtds_hash) == -1) {
2587 		mdb_warn("failed to read hash at %p",
2588 		    (uintptr_t)dstate.dtds_hash);
2589 		mdb_free(data->dtdvd_hash, hsize);
2590 		mdb_free(data, sizeof (dtrace_dynvar_data_t));
2591 		return (WALK_ERR);
2592 	}
2593 
2594 	wsp->walk_data = data;
2595 	return (WALK_NEXT);
2596 }
2597 
2598 int
2599 dtrace_dynvar_step(mdb_walk_state_t *wsp)
2600 {
2601 	dtrace_dynvar_data_t *data = wsp->walk_data;
2602 	dtrace_dynvar_t dynvar, *dvar;
2603 	size_t dvarsize;
2604 	uintptr_t addr;
2605 	int nkeys;
2606 
2607 	while ((addr = data->dtdvd_next) == NULL) {
2608 		if (data->dtdvd_ndx == data->dtdvd_hashsize)
2609 			return (WALK_DONE);
2610 
2611 		data->dtdvd_next =
2612 		    (uintptr_t)data->dtdvd_hash[data->dtdvd_ndx++].dtdh_chain;
2613 	}
2614 
2615 	if (mdb_vread(&dynvar, sizeof (dynvar), addr) == -1) {
2616 		mdb_warn("failed to read dtrace_dynvar_t at %p", addr);
2617 		return (WALK_ERR);
2618 	}
2619 
2620 	/*
2621 	 * Now we need to allocate the correct size.
2622 	 */
2623 	nkeys = dynvar.dtdv_tuple.dtt_nkeys;
2624 	dvarsize = (uintptr_t)&dynvar.dtdv_tuple.dtt_key[nkeys] -
2625 	    (uintptr_t)&dynvar;
2626 
2627 	dvar = alloca(dvarsize);
2628 
2629 	if (mdb_vread(dvar, dvarsize, addr) == -1) {
2630 		mdb_warn("failed to read dtrace_dynvar_t at %p", addr);
2631 		return (WALK_ERR);
2632 	}
2633 
2634 	data->dtdvd_next = (uintptr_t)dynvar.dtdv_next;
2635 
2636 	return (wsp->walk_callback(addr, dvar, wsp->walk_cbdata));
2637 }
2638 
2639 void
2640 dtrace_dynvar_fini(mdb_walk_state_t *wsp)
2641 {
2642 	dtrace_dynvar_data_t *data = wsp->walk_data;
2643 	size_t hsize;
2644 
2645 	hsize = data->dtdvd_hashsize * sizeof (dtrace_dynvar_t *);
2646 	mdb_free(data->dtdvd_hash, hsize);
2647 	mdb_free(data, sizeof (dtrace_dynvar_data_t));
2648 }
2649 
2650 typedef struct dtrace_hashstat_data {
2651 	size_t *dthsd_counts;
2652 	size_t dthsd_hashsize;
2653 	char *dthsd_data;
2654 	size_t dthsd_size;
2655 	int dthsd_header;
2656 } dtrace_hashstat_data_t;
2657 
2658 typedef void (*dtrace_hashstat_func_t)(dtrace_hashstat_data_t *);
2659 
2660 static void
2661 dtrace_hashstat_additive(dtrace_hashstat_data_t *data)
2662 {
2663 	int i;
2664 	int hval = 0;
2665 
2666 	for (i = 0; i < data->dthsd_size; i++)
2667 		hval += data->dthsd_data[i];
2668 
2669 	data->dthsd_counts[hval % data->dthsd_hashsize]++;
2670 }
2671 
2672 static void
2673 dtrace_hashstat_shifty(dtrace_hashstat_data_t *data)
2674 {
2675 	uint64_t hval = 0;
2676 	int i;
2677 
2678 	if (data->dthsd_size < sizeof (uint64_t)) {
2679 		dtrace_hashstat_additive(data);
2680 		return;
2681 	}
2682 
2683 	for (i = 0; i < data->dthsd_size; i += sizeof (uint64_t)) {
2684 		/* LINTED - alignment */
2685 		uint64_t val = *((uint64_t *)&data->dthsd_data[i]);
2686 
2687 		hval += (val & ((1 << NBBY) - 1)) +
2688 		    ((val >> NBBY) & ((1 << NBBY) - 1)) +
2689 		    ((val >> (NBBY << 1)) & ((1 << NBBY) - 1)) +
2690 		    ((val >> (NBBY << 2)) & ((1 << NBBY) - 1)) +
2691 		    (val & USHRT_MAX) + (val >> (NBBY << 1) & USHRT_MAX);
2692 	}
2693 
2694 	data->dthsd_counts[hval % data->dthsd_hashsize]++;
2695 }
2696 
2697 static void
2698 dtrace_hashstat_knuth(dtrace_hashstat_data_t *data)
2699 {
2700 	int i;
2701 	int hval = data->dthsd_size;
2702 
2703 	for (i = 0; i < data->dthsd_size; i++)
2704 		hval = (hval << 4) ^ (hval >> 28) ^ data->dthsd_data[i];
2705 
2706 	data->dthsd_counts[hval % data->dthsd_hashsize]++;
2707 }
2708 
2709 static void
2710 dtrace_hashstat_oneatatime(dtrace_hashstat_data_t *data)
2711 {
2712 	int i;
2713 	uint32_t hval = 0;
2714 
2715 	for (i = 0; i < data->dthsd_size; i++) {
2716 		hval += data->dthsd_data[i];
2717 		hval += (hval << 10);
2718 		hval ^= (hval >> 6);
2719 	}
2720 
2721 	hval += (hval << 3);
2722 	hval ^= (hval >> 11);
2723 	hval += (hval << 15);
2724 
2725 	data->dthsd_counts[hval % data->dthsd_hashsize]++;
2726 }
2727 
2728 static void
2729 dtrace_hashstat_fnv(dtrace_hashstat_data_t *data)
2730 {
2731 	static const uint32_t prime = 0x01000193;
2732 	uint32_t hval = 0;
2733 	int i;
2734 
2735 	for (i = 0; i < data->dthsd_size; i++) {
2736 		hval *= prime;
2737 		hval ^= data->dthsd_data[i];
2738 	}
2739 
2740 	data->dthsd_counts[hval % data->dthsd_hashsize]++;
2741 }
2742 
2743 static void
2744 dtrace_hashstat_stats(char *name, dtrace_hashstat_data_t *data)
2745 {
2746 	size_t nz = 0, i;
2747 	int longest = 0;
2748 	size_t ttl = 0;
2749 	double sum = 0.0;
2750 	double avg;
2751 	uint_t util, stddev;
2752 
2753 	if (!data->dthsd_header) {
2754 		mdb_printf("%15s %11s %11s %11s %11s %11s\n", "NAME",
2755 		    "HASHSIZE", "%UTIL", "LONGEST", "AVERAGE", "STDDEV");
2756 		data->dthsd_header = 1;
2757 	}
2758 
2759 	for (i = 0; i < data->dthsd_hashsize; i++) {
2760 		if (data->dthsd_counts[i] != 0) {
2761 			nz++;
2762 
2763 			if (data->dthsd_counts[i] > longest)
2764 				longest = data->dthsd_counts[i];
2765 
2766 			ttl += data->dthsd_counts[i];
2767 		}
2768 	}
2769 
2770 	if (nz == 0) {
2771 		mdb_printf("%15s %11d %11s %11s %11s %11s\n", name,
2772 		    data->dthsd_hashsize, "-", "-", "-", "-");
2773 		return;
2774 	}
2775 
2776 	avg = (double)ttl / (double)nz;
2777 
2778 	for (i = 0; i < data->dthsd_hashsize; i++) {
2779 		double delta = (double)data->dthsd_counts[i] - avg;
2780 
2781 		if (data->dthsd_counts[i] == 0)
2782 			continue;
2783 
2784 		sum += delta * delta;
2785 	}
2786 
2787 	util = (nz * 1000) / data->dthsd_hashsize;
2788 	stddev = (uint_t)sqrt(sum / (double)nz) * 10;
2789 
2790 	mdb_printf("%15s %11d %9u.%1u %11d %11d %9u.%1u\n", name,
2791 	    data->dthsd_hashsize, util / 10, util % 10, longest, ttl / nz,
2792 	    stddev / 10, stddev % 10);
2793 }
2794 
2795 static struct dtrace_hashstat {
2796 	char *dths_name;
2797 	dtrace_hashstat_func_t dths_func;
2798 } _dtrace_hashstat[] = {
2799 	{ "<actual>", NULL },
2800 	{ "additive", dtrace_hashstat_additive },
2801 	{ "shifty", dtrace_hashstat_shifty },
2802 	{ "knuth", dtrace_hashstat_knuth },
2803 	{ "one-at-a-time", dtrace_hashstat_oneatatime },
2804 	{ "fnv", dtrace_hashstat_fnv },
2805 	{ NULL, 0 }
2806 };
2807 
2808 typedef struct dtrace_aggstat_data {
2809 	dtrace_hashstat_data_t dtagsd_hash;
2810 	dtrace_hashstat_func_t dtagsd_func;
2811 } dtrace_aggstat_data_t;
2812 
2813 static int
2814 dtrace_aggstat_walk(uintptr_t addr, dtrace_aggkey_t *key,
2815     dtrace_aggstat_data_t *data)
2816 {
2817 	dtrace_hashstat_data_t *hdata = &data->dtagsd_hash;
2818 	size_t size;
2819 
2820 	if (data->dtagsd_func == NULL) {
2821 		size_t bucket = key->dtak_hashval % hdata->dthsd_hashsize;
2822 
2823 		hdata->dthsd_counts[bucket]++;
2824 		return (WALK_NEXT);
2825 	}
2826 
2827 	/*
2828 	 * We need to read the data.
2829 	 */
2830 	size = key->dtak_size - sizeof (dtrace_aggid_t);
2831 	addr = (uintptr_t)key->dtak_data + sizeof (dtrace_aggid_t);
2832 	hdata->dthsd_data = alloca(size);
2833 	hdata->dthsd_size = size;
2834 
2835 	if (mdb_vread(hdata->dthsd_data, size, addr) == -1) {
2836 		mdb_warn("couldn't read data at %p", addr);
2837 		return (WALK_ERR);
2838 	}
2839 
2840 	data->dtagsd_func(hdata);
2841 
2842 	return (WALK_NEXT);
2843 }
2844 
2845 /*ARGSUSED*/
2846 int
2847 dtrace_aggstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2848 {
2849 	dtrace_buffer_t buf;
2850 	uintptr_t aaddr;
2851 	dtrace_aggbuffer_t agb;
2852 	size_t hsize, i, actual, prime, evenpow;
2853 	dtrace_aggstat_data_t data;
2854 	dtrace_hashstat_data_t *hdata = &data.dtagsd_hash;
2855 
2856 	bzero(&data, sizeof (data));
2857 
2858 	if (!(flags & DCMD_ADDRSPEC))
2859 		return (DCMD_USAGE);
2860 
2861 	if (mdb_vread(&buf, sizeof (buf), addr) == -1) {
2862 		mdb_warn("failed to read aggregation buffer at %p", addr);
2863 		return (DCMD_ERR);
2864 	}
2865 
2866 	aaddr = (uintptr_t)buf.dtb_tomax +
2867 	    buf.dtb_size - sizeof (dtrace_aggbuffer_t);
2868 
2869 	if (mdb_vread(&agb, sizeof (agb), aaddr) == -1) {
2870 		mdb_warn("failed to read dtrace_aggbuffer_t at %p", aaddr);
2871 		return (DCMD_ERR);
2872 	}
2873 
2874 	hsize = (actual = agb.dtagb_hashsize) * sizeof (size_t);
2875 	hdata->dthsd_counts = mdb_alloc(hsize, UM_SLEEP | UM_GC);
2876 
2877 	/*
2878 	 * Now pick the largest prime smaller than the hash size.  (If the
2879 	 * existing size is prime, we'll pick a smaller prime just for the
2880 	 * hell of it.)
2881 	 */
2882 	for (prime = agb.dtagb_hashsize - 1; prime > 7; prime--) {
2883 		size_t limit = prime / 7;
2884 
2885 		for (i = 2; i < limit; i++) {
2886 			if ((prime % i) == 0)
2887 				break;
2888 		}
2889 
2890 		if (i == limit)
2891 			break;
2892 	}
2893 
2894 	/*
2895 	 * And now we want to pick the largest power of two smaller than the
2896 	 * hashsize.
2897 	 */
2898 	for (i = 0; (1 << i) < agb.dtagb_hashsize; i++)
2899 		continue;
2900 
2901 	evenpow = (1 << (i - 1));
2902 
2903 	for (i = 0; _dtrace_hashstat[i].dths_name != NULL; i++) {
2904 		data.dtagsd_func = _dtrace_hashstat[i].dths_func;
2905 
2906 		hdata->dthsd_hashsize = actual;
2907 		hsize = hdata->dthsd_hashsize * sizeof (size_t);
2908 		bzero(hdata->dthsd_counts, hsize);
2909 
2910 		if (mdb_pwalk("dtrace_aggkey",
2911 		    (mdb_walk_cb_t)dtrace_aggstat_walk, &data, addr) == -1) {
2912 			mdb_warn("failed to walk dtrace_aggkey at %p", addr);
2913 			return (DCMD_ERR);
2914 		}
2915 
2916 		dtrace_hashstat_stats(_dtrace_hashstat[i].dths_name, hdata);
2917 
2918 		/*
2919 		 * If we were just printing the actual value, we won't try
2920 		 * any of the sizing experiments.
2921 		 */
2922 		if (data.dtagsd_func == NULL)
2923 			continue;
2924 
2925 		hdata->dthsd_hashsize = prime;
2926 		hsize = hdata->dthsd_hashsize * sizeof (size_t);
2927 		bzero(hdata->dthsd_counts, hsize);
2928 
2929 		if (mdb_pwalk("dtrace_aggkey",
2930 		    (mdb_walk_cb_t)dtrace_aggstat_walk, &data, addr) == -1) {
2931 			mdb_warn("failed to walk dtrace_aggkey at %p", addr);
2932 			return (DCMD_ERR);
2933 		}
2934 
2935 		dtrace_hashstat_stats(_dtrace_hashstat[i].dths_name, hdata);
2936 
2937 		hdata->dthsd_hashsize = evenpow;
2938 		hsize = hdata->dthsd_hashsize * sizeof (size_t);
2939 		bzero(hdata->dthsd_counts, hsize);
2940 
2941 		if (mdb_pwalk("dtrace_aggkey",
2942 		    (mdb_walk_cb_t)dtrace_aggstat_walk, &data, addr) == -1) {
2943 			mdb_warn("failed to walk dtrace_aggkey at %p", addr);
2944 			return (DCMD_ERR);
2945 		}
2946 
2947 		dtrace_hashstat_stats(_dtrace_hashstat[i].dths_name, hdata);
2948 	}
2949 
2950 	return (DCMD_OK);
2951 }
2952 
2953 /*ARGSUSED*/
2954 static int
2955 dtrace_dynstat_walk(uintptr_t addr, dtrace_dynvar_t *dynvar,
2956     dtrace_aggstat_data_t *data)
2957 {
2958 	dtrace_hashstat_data_t *hdata = &data->dtagsd_hash;
2959 	dtrace_tuple_t *tuple = &dynvar->dtdv_tuple;
2960 	dtrace_key_t *key = tuple->dtt_key;
2961 	size_t size = 0, offs = 0;
2962 	int i, nkeys = tuple->dtt_nkeys;
2963 	char *buf;
2964 
2965 	if (data->dtagsd_func == NULL) {
2966 		size_t bucket = dynvar->dtdv_hashval % hdata->dthsd_hashsize;
2967 
2968 		hdata->dthsd_counts[bucket]++;
2969 		return (WALK_NEXT);
2970 	}
2971 
2972 	/*
2973 	 * We want to hand the hashing algorithm a contiguous buffer.  First
2974 	 * run through the tuple and determine the size.
2975 	 */
2976 	for (i = 0; i < nkeys; i++) {
2977 		if (key[i].dttk_size == 0) {
2978 			size += sizeof (uint64_t);
2979 		} else {
2980 			size += key[i].dttk_size;
2981 		}
2982 	}
2983 
2984 	buf = alloca(size);
2985 
2986 	/*
2987 	 * Now go back through the tuple and copy the data into the buffer.
2988 	 */
2989 	for (i = 0; i < nkeys; i++) {
2990 		if (key[i].dttk_size == 0) {
2991 			bcopy(&key[i].dttk_value, &buf[offs],
2992 			    sizeof (uint64_t));
2993 			offs += sizeof (uint64_t);
2994 		} else {
2995 			if (mdb_vread(&buf[offs], key[i].dttk_size,
2996 			    key[i].dttk_value) == -1) {
2997 				mdb_warn("couldn't read tuple data at %p",
2998 				    key[i].dttk_value);
2999 				return (WALK_ERR);
3000 			}
3001 
3002 			offs += key[i].dttk_size;
3003 		}
3004 	}
3005 
3006 	hdata->dthsd_data = buf;
3007 	hdata->dthsd_size = size;
3008 
3009 	data->dtagsd_func(hdata);
3010 
3011 	return (WALK_NEXT);
3012 }
3013 
3014 /*ARGSUSED*/
3015 int
3016 dtrace_dynstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3017 {
3018 	dtrace_dstate_t dstate;
3019 	size_t hsize, i, actual, prime;
3020 	dtrace_aggstat_data_t data;
3021 	dtrace_hashstat_data_t *hdata = &data.dtagsd_hash;
3022 
3023 	bzero(&data, sizeof (data));
3024 
3025 	if (!(flags & DCMD_ADDRSPEC))
3026 		return (DCMD_USAGE);
3027 
3028 	if (mdb_vread(&dstate, sizeof (dstate), addr) == -1) {
3029 		mdb_warn("failed to read dynamic variable state at %p", addr);
3030 		return (DCMD_ERR);
3031 	}
3032 
3033 	hsize = (actual = dstate.dtds_hashsize) * sizeof (size_t);
3034 	hdata->dthsd_counts = mdb_alloc(hsize, UM_SLEEP | UM_GC);
3035 
3036 	/*
3037 	 * Now pick the largest prime smaller than the hash size.  (If the
3038 	 * existing size is prime, we'll pick a smaller prime just for the
3039 	 * hell of it.)
3040 	 */
3041 	for (prime = dstate.dtds_hashsize - 1; prime > 7; prime--) {
3042 		size_t limit = prime / 7;
3043 
3044 		for (i = 2; i < limit; i++) {
3045 			if ((prime % i) == 0)
3046 				break;
3047 		}
3048 
3049 		if (i == limit)
3050 			break;
3051 	}
3052 
3053 	for (i = 0; _dtrace_hashstat[i].dths_name != NULL; i++) {
3054 		data.dtagsd_func = _dtrace_hashstat[i].dths_func;
3055 
3056 		hdata->dthsd_hashsize = actual;
3057 		hsize = hdata->dthsd_hashsize * sizeof (size_t);
3058 		bzero(hdata->dthsd_counts, hsize);
3059 
3060 		if (mdb_pwalk("dtrace_dynvar",
3061 		    (mdb_walk_cb_t)dtrace_dynstat_walk, &data, addr) == -1) {
3062 			mdb_warn("failed to walk dtrace_dynvar at %p", addr);
3063 			return (DCMD_ERR);
3064 		}
3065 
3066 		dtrace_hashstat_stats(_dtrace_hashstat[i].dths_name, hdata);
3067 
3068 		/*
3069 		 * If we were just printing the actual value, we won't try
3070 		 * any of the sizing experiments.
3071 		 */
3072 		if (data.dtagsd_func == NULL)
3073 			continue;
3074 
3075 		hdata->dthsd_hashsize = prime;
3076 		hsize = hdata->dthsd_hashsize * sizeof (size_t);
3077 		bzero(hdata->dthsd_counts, hsize);
3078 
3079 		if (mdb_pwalk("dtrace_dynvar",
3080 		    (mdb_walk_cb_t)dtrace_dynstat_walk, &data, addr) == -1) {
3081 			mdb_warn("failed to walk dtrace_aggkey at %p", addr);
3082 			return (DCMD_ERR);
3083 		}
3084 
3085 		dtrace_hashstat_stats(_dtrace_hashstat[i].dths_name, hdata);
3086 	}
3087 
3088 	return (DCMD_OK);
3089 }
3090 
3091 static int
3092 dof_sect_strtab(uintptr_t addr, dof_sec_t *sec)
3093 {
3094 	char *strtab;
3095 	size_t sz, i;
3096 
3097 	sz = (size_t)sec->dofs_size;
3098 	strtab = mdb_alloc(sz, UM_SLEEP | UM_GC);
3099 	if (mdb_vread(strtab, sz, addr + sec->dofs_offset) != sz) {
3100 		mdb_warn("failed to read string table");
3101 		return (1);
3102 	}
3103 
3104 	mdb_printf("size = %lx\n", sz);
3105 
3106 	for (i = 0; i < sz; i++) {
3107 		if (strtab[i] == '\0')
3108 			mdb_printf("\\0");
3109 		else
3110 			mdb_printf("%c", strtab[i]);
3111 	}
3112 
3113 	mdb_printf("\n");
3114 
3115 	return (0);
3116 }
3117 
3118 static int
3119 dof_sect_provider(uintptr_t addr, dof_sec_t *sec, dof_sec_t *dofs)
3120 {
3121 	dof_provider_t pv;
3122 	dof_probe_t *pb;
3123 	char *strtab;
3124 	uint32_t *offs;
3125 	uint8_t *args = NULL;
3126 	size_t sz;
3127 	int i, j;
3128 	dof_stridx_t narg, xarg;
3129 
3130 	if (mdb_vread(&pv, sizeof (dof_provider_t),
3131 	    addr + sec->dofs_offset) != sizeof (dof_provider_t)) {
3132 		mdb_warn("failed to read DOF provider");
3133 		return (-1);
3134 	}
3135 
3136 	sz = dofs[pv.dofpv_strtab].dofs_size;
3137 	strtab = mdb_alloc(sz, UM_SLEEP | UM_GC);
3138 	if (mdb_vread(strtab, sz, addr +
3139 	    dofs[pv.dofpv_strtab].dofs_offset) != sz) {
3140 		mdb_warn("failed to read string table");
3141 		return (-1);
3142 	}
3143 
3144 	mdb_printf("%lx provider %s {\n", (ulong_t)(addr + sec->dofs_offset),
3145 	    strtab + pv.dofpv_name);
3146 
3147 	sz = dofs[pv.dofpv_prargs].dofs_size;
3148 	if (sz != 0) {
3149 		args = mdb_alloc(sz, UM_SLEEP | UM_GC);
3150 		if (mdb_vread(args, sz, addr +
3151 		    dofs[pv.dofpv_prargs].dofs_offset) != sz) {
3152 			mdb_warn("failed to read args");
3153 			return (-1);
3154 		}
3155 	}
3156 
3157 	sz = dofs[pv.dofpv_proffs].dofs_size;
3158 	offs = mdb_alloc(sz, UM_SLEEP | UM_GC);
3159 	if (mdb_vread(offs, sz, addr + dofs[pv.dofpv_proffs].dofs_offset)
3160 	    != sz) {
3161 		mdb_warn("failed to read offs");
3162 		return (-1);
3163 	}
3164 
3165 	sz = dofs[pv.dofpv_probes].dofs_size;
3166 	pb = mdb_alloc(sz, UM_SLEEP | UM_GC);
3167 	if (mdb_vread(pb, sz, addr + dofs[pv.dofpv_probes].dofs_offset) != sz) {
3168 		mdb_warn("failed to read probes");
3169 		return (-1);
3170 	}
3171 
3172 	(void) mdb_inc_indent(2);
3173 
3174 	for (i = 0; i < sz / dofs[pv.dofpv_probes].dofs_entsize; i++) {
3175 		mdb_printf("%lx probe %s:%s {\n", (ulong_t)(addr +
3176 		    dofs[pv.dofpv_probes].dofs_offset +
3177 		    i * dofs[pv.dofpv_probes].dofs_entsize),
3178 		    strtab + pb[i].dofpr_func,
3179 		    strtab + pb[i].dofpr_name);
3180 
3181 		(void) mdb_inc_indent(2);
3182 		mdb_printf("addr: %p\n", (ulong_t)pb[i].dofpr_addr);
3183 		mdb_printf("offs: ");
3184 		for (j = 0; j < pb[i].dofpr_noffs; j++) {
3185 			mdb_printf("%s %x", "," + (j == 0),
3186 			    offs[pb[i].dofpr_offidx + j]);
3187 		}
3188 		mdb_printf("\n");
3189 
3190 		mdb_printf("nargs:");
3191 		narg = pb[i].dofpr_nargv;
3192 		for (j = 0; j < pb[i].dofpr_nargc; j++) {
3193 			mdb_printf("%s %s", "," + (j == 0), strtab + narg);
3194 			narg += strlen(strtab + narg) + 1;
3195 		}
3196 		mdb_printf("\n");
3197 		mdb_printf("xargs:");
3198 		xarg = pb[i].dofpr_xargv;
3199 		for (j = 0; j < pb[i].dofpr_xargc; j++) {
3200 			mdb_printf("%s %s", "," + (j == 0), strtab + xarg);
3201 			xarg += strlen(strtab + xarg) + 1;
3202 		}
3203 		mdb_printf("\n");
3204 		mdb_printf("map:  ");
3205 		for (j = 0; j < pb[i].dofpr_xargc; j++) {
3206 			mdb_printf("%s %d->%d", "," + (j == 0),
3207 			    args[pb[i].dofpr_argidx + j], j);
3208 		}
3209 
3210 		(void) mdb_dec_indent(2);
3211 		mdb_printf("\n}\n");
3212 	}
3213 
3214 	(void) mdb_dec_indent(2);
3215 	mdb_printf("}\n");
3216 
3217 	return (0);
3218 }
3219 
3220 static int
3221 dof_sect_prargs(uintptr_t addr, dof_sec_t *sec)
3222 {
3223 	int i;
3224 	uint8_t arg;
3225 
3226 	for (i = 0; i < sec->dofs_size; i++) {
3227 		if (mdb_vread(&arg, sizeof (arg),
3228 		    addr + sec->dofs_offset + i) != sizeof (arg)) {
3229 			mdb_warn("failed to read argument");
3230 			return (1);
3231 		}
3232 
3233 		mdb_printf("%d ", arg);
3234 
3235 		if (i % 20 == 19)
3236 			mdb_printf("\n");
3237 	}
3238 
3239 	mdb_printf("\n");
3240 
3241 	return (0);
3242 }
3243 
3244 /*ARGSUSED*/
3245 static int
3246 dofdump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3247 {
3248 	dof_hdr_t dofh;
3249 	dof_sec_t *dofs;
3250 	int i;
3251 
3252 	if (mdb_vread(&dofh, sizeof (dof_hdr_t), addr) != sizeof (dof_hdr_t)) {
3253 		mdb_warn("failed to read DOF header");
3254 		return (DCMD_ERR);
3255 	}
3256 
3257 	dofs = mdb_alloc(sizeof (dof_sec_t) * dofh.dofh_secnum,
3258 	    UM_SLEEP | UM_GC);
3259 
3260 	for (i = 0; i < dofh.dofh_secnum; i++) {
3261 		if (mdb_vread(&dofs[i], sizeof (dof_sec_t), dofh.dofh_secoff +
3262 		    addr + i * dofh.dofh_secsize) != sizeof (dof_sec_t)) {
3263 			mdb_warn("failed to read DOF sections");
3264 			return (DCMD_ERR);
3265 		}
3266 	}
3267 
3268 	for (i = 0; i < dofh.dofh_secnum; i++) {
3269 		mdb_printf("%lx Section %d: %s\n", (ulong_t)(dofh.dofh_secoff +
3270 		    addr + i * dofh.dofh_secsize), i,
3271 		    dof_sec_typename(dofs[i].dofs_type));
3272 
3273 		(void) mdb_inc_indent(2);
3274 		switch (dofs[i].dofs_type) {
3275 		case DOF_SECT_PROVIDER:
3276 			(void) dof_sect_provider(addr, &dofs[i], dofs);
3277 			break;
3278 		case DOF_SECT_STRTAB:
3279 			(void) dof_sect_strtab(addr, &dofs[i]);
3280 			break;
3281 		case DOF_SECT_PRARGS:
3282 			(void) dof_sect_prargs(addr, &dofs[i]);
3283 			break;
3284 		}
3285 		(void) mdb_dec_indent(2);
3286 
3287 		mdb_printf("\n");
3288 	}
3289 
3290 	return (DCMD_OK);
3291 }
3292 
3293 static const mdb_dcmd_t dcmds[] = {
3294 	{ "id2probe", ":", "translate a dtrace_id_t to a dtrace_probe_t",
3295 	    id2probe },
3296 	{ "difinstr", ":", "disassemble a DIF instruction", difinstr },
3297 	{ "difo", ":", "print a DIF object", difo },
3298 	{ "dof_hdr", "?", "print a DOF header", dof_hdr },
3299 	{ "dof_sec", ":", "print a DOF section header", dof_sec },
3300 	{ "dof_ecbdesc", ":", "print a DOF ecbdesc", dof_ecbdesc },
3301 	{ "dof_probedesc", ":", "print a DOF probedesc", dof_probedesc },
3302 	{ "dof_actdesc", ":", "print a DOF actdesc", dof_actdesc },
3303 	{ "dof_relohdr", ":", "print a DOF relocation header", dof_relohdr },
3304 	{ "dof_relodesc", ":", "print a DOF relodesc", dof_relodesc },
3305 	{ "dofdump", ":", "dump DOF", dofdump },
3306 	{ "dtrace", ":[-c cpu]", "print dtrace(1M)-like output",
3307 	    dtrace, dtrace_help },
3308 	{ "dtrace_errhash", ":", "print DTrace error hash", dtrace_errhash },
3309 	{ "dtrace_helptrace", ":", "print DTrace helper trace",
3310 	    dtrace_helptrace },
3311 	{ "dtrace_state", ":", "print active DTrace consumers", dtrace_state,
3312 	    dtrace_state_help },
3313 	{ "dtrace_aggstat", ":",
3314 	    "print DTrace aggregation hash statistics", dtrace_aggstat },
3315 	{ "dtrace_dynstat", ":",
3316 	    "print DTrace dynamic variable hash statistics", dtrace_dynstat },
3317 	{ NULL }
3318 };
3319 
3320 static const mdb_walker_t walkers[] = {
3321 	{ "dof_sec", "walk DOF section header table given header address",
3322 		dof_sec_walk_init, dof_sec_walk_step, dof_sec_walk_fini },
3323 	{ "dtrace_errhash", "walk hash of DTrace error messasges",
3324 		dtrace_errhash_init, dtrace_errhash_step },
3325 	{ "dtrace_helptrace", "walk DTrace helper trace entries",
3326 		dtrace_helptrace_init, dtrace_helptrace_step },
3327 	{ "dtrace_state", "walk DTrace per-consumer softstate",
3328 		dtrace_state_init, dtrace_state_step },
3329 	{ "dtrace_aggkey", "walk DTrace aggregation keys",
3330 		dtrace_aggkey_init, dtrace_aggkey_step, dtrace_aggkey_fini },
3331 	{ "dtrace_dynvar", "walk DTrace dynamic variables",
3332 		dtrace_dynvar_init, dtrace_dynvar_step, dtrace_dynvar_fini },
3333 	{ NULL }
3334 };
3335 
3336 static const mdb_modinfo_t modinfo = {
3337 	MDB_API_VERSION, dcmds, walkers
3338 };
3339 
3340 const mdb_modinfo_t *
3341 _mdb_init(void)
3342 {
3343 	return (&modinfo);
3344 }
3345