xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_cmds.c (revision 1700af3add37a7b6db478d0876536849c3f691fe)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright (c) 2012 by Delphix. All rights reserved.
29  * Copyright (c) 2018 Joyent, Inc. All rights reserved.
30  * Copyright (c) 2013 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
31  * Copyright (c) 2015, 2017 by Delphix. All rights reserved.
32  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
33  */
34 
35 #include <sys/elf.h>
36 #include <sys/elf_SPARC.h>
37 
38 #include <libproc.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 #include <alloca.h>
44 #include <libctf.h>
45 #include <ctype.h>
46 
47 #include <mdb/mdb_string.h>
48 #include <mdb/mdb_argvec.h>
49 #include <mdb/mdb_nv.h>
50 #include <mdb/mdb_fmt.h>
51 #include <mdb/mdb_target.h>
52 #include <mdb/mdb_err.h>
53 #include <mdb/mdb_debug.h>
54 #include <mdb/mdb_conf.h>
55 #include <mdb/mdb_module.h>
56 #include <mdb/mdb_modapi.h>
57 #include <mdb/mdb_stdlib.h>
58 #include <mdb/mdb_lex.h>
59 #include <mdb/mdb_io_impl.h>
60 #include <mdb/mdb_help.h>
61 #include <mdb/mdb_disasm.h>
62 #include <mdb/mdb_frame.h>
63 #include <mdb/mdb_evset.h>
64 #include <mdb/mdb_print.h>
65 #include <mdb/mdb_nm.h>
66 #include <mdb/mdb_set.h>
67 #include <mdb/mdb_demangle.h>
68 #include <mdb/mdb_ctf.h>
69 #include <mdb/mdb_whatis.h>
70 #include <mdb/mdb_whatis_impl.h>
71 #include <mdb/mdb_macalias.h>
72 #include <mdb/mdb_tab.h>
73 #include <mdb/mdb_typedef.h>
74 #ifdef _KMDB
75 #include <kmdb/kmdb_kdi.h>
76 #endif
77 #include <mdb/mdb.h>
78 
79 #ifdef __sparc
80 #define	SETHI_MASK	0xc1c00000
81 #define	SETHI_VALUE	0x01000000
82 
83 #define	IS_SETHI(machcode)	(((machcode) & SETHI_MASK) == SETHI_VALUE)
84 
85 #define	OP(machcode)	((machcode) >> 30)
86 #define	OP3(machcode)	(((machcode) >> 19) & 0x3f)
87 #define	RD(machcode)	(((machcode) >> 25) & 0x1f)
88 #define	RS1(machcode)	(((machcode) >> 14) & 0x1f)
89 #define	I(machcode)	(((machcode) >> 13) & 0x01)
90 
91 #define	IMM13(machcode)	((machcode) & 0x1fff)
92 #define	IMM22(machcode)	((machcode) & 0x3fffff)
93 
94 #define	OP_ARITH_MEM_MASK	0x2
95 #define	OP_ARITH		0x2
96 #define	OP_MEM			0x3
97 
98 #define	OP3_CC_MASK		0x10
99 #define	OP3_COMPLEX_MASK	0x20
100 
101 #define	OP3_ADD			0x00
102 #define	OP3_OR			0x02
103 #define	OP3_XOR			0x03
104 
105 #ifndef	R_O7
106 #define	R_O7	0xf
107 #endif
108 #endif /* __sparc */
109 
110 static mdb_tgt_addr_t
111 write_uint8(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
112 {
113 	uint8_t o, n = (uint8_t)ull;
114 
115 	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
116 	    addr) == -1)
117 		return (addr);
118 
119 	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
120 		return (addr);
121 
122 	if (rdback) {
123 		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
124 			return (addr);
125 
126 		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8x=%8T0x%x\n",
127 		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
128 	}
129 
130 	return (addr + sizeof (n));
131 }
132 
133 static mdb_tgt_addr_t
134 write_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
135 {
136 	uint16_t o, n = (uint16_t)ull;
137 
138 	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
139 	    addr) == -1)
140 		return (addr);
141 
142 	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
143 		return (addr);
144 
145 	if (rdback) {
146 		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
147 			return (addr);
148 
149 		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8hx=%8T0x%hx\n",
150 		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
151 	}
152 
153 	return (addr + sizeof (n));
154 }
155 
156 static mdb_tgt_addr_t
157 write_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
158 {
159 	uint32_t o, n = (uint32_t)ull;
160 
161 	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
162 	    addr) == -1)
163 		return (addr);
164 
165 	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
166 		return (addr);
167 
168 	if (rdback) {
169 		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
170 			return (addr);
171 
172 		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#16x=%8T0x%x\n",
173 		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
174 	}
175 
176 	return (addr + sizeof (n));
177 }
178 
179 static mdb_tgt_addr_t
180 write_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
181 {
182 	uint64_t o;
183 
184 	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
185 	    addr) == -1)
186 		return (addr);
187 
188 	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
189 		return (addr);
190 
191 	if (rdback) {
192 		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
193 			return (addr);
194 
195 		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#24llx=%8T0x%llx\n",
196 		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
197 	}
198 
199 	return (addr + sizeof (n));
200 }
201 
202 /*
203  * Writes to objects of size 1, 2, 4, or 8 bytes. The function
204  * doesn't care if the object is a number or not (e.g. it could
205  * be a byte array, or a struct) as long as the size of the write
206  * is one of the aforementioned ones.
207  */
208 static mdb_tgt_addr_t
209 write_var_uint(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, size_t size,
210     uint_t rdback)
211 {
212 	if (size < sizeof (uint64_t)) {
213 		uint64_t max_num = 1ULL << (size * NBBY);
214 
215 		if (val >= max_num) {
216 			uint64_t write_len = 0;
217 
218 			/* count bytes needed for val */
219 			while (val != 0) {
220 				write_len++;
221 				val >>= NBBY;
222 			}
223 
224 			mdb_warn("value too big for the length of the write: "
225 			    "supplied %llu bytes but maximum is %llu bytes\n",
226 			    (u_longlong_t)write_len, (u_longlong_t)size);
227 			return (addr);
228 		}
229 	}
230 
231 	switch (size) {
232 	case 1:
233 		return (write_uint8(as, addr, val, rdback));
234 	case 2:
235 		return (write_uint16(as, addr, val, rdback));
236 	case 4:
237 		return (write_uint32(as, addr, val, rdback));
238 	case 8:
239 		return (write_uint64(as, addr, val, rdback));
240 	default:
241 		mdb_warn("writes of size %u are not supported\n ", size);
242 		return (addr);
243 	}
244 }
245 
246 static mdb_tgt_addr_t
247 write_ctf_uint(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
248 {
249 	mdb_ctf_id_t mid;
250 	size_t size;
251 	ssize_t type_size;
252 	int kind;
253 
254 	if (mdb_ctf_lookup_by_addr(addr, &mid) != 0) {
255 		mdb_warn("no CTF data found at this address\n");
256 		return (addr);
257 	}
258 
259 	kind = mdb_ctf_type_kind(mid);
260 	if (kind == CTF_ERR) {
261 		mdb_warn("CTF data found but type kind could not be read");
262 		return (addr);
263 	}
264 
265 	if (kind == CTF_K_TYPEDEF) {
266 		mdb_ctf_id_t temp_id;
267 		if (mdb_ctf_type_resolve(mid, &temp_id) != 0) {
268 			mdb_warn("failed to resolve type");
269 			return (addr);
270 		}
271 		kind = mdb_ctf_type_kind(temp_id);
272 	}
273 
274 	if (kind != CTF_K_INTEGER && kind != CTF_K_POINTER &&
275 	    kind != CTF_K_ENUM) {
276 		mdb_warn("CTF type should be integer, pointer, or enum\n");
277 		return (addr);
278 	}
279 
280 	type_size = mdb_ctf_type_size(mid);
281 	if (type_size < 0) {
282 		mdb_warn("CTF data found but size could not be read");
283 		return (addr);
284 	}
285 	size = type_size;
286 
287 	return (write_var_uint(as, addr, n, size, rdback));
288 }
289 
290 static int
291 write_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr,
292     int argc, const mdb_arg_t *argv)
293 {
294 	mdb_tgt_addr_t (*write_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
295 	    uint64_t, uint_t);
296 	mdb_tgt_addr_t naddr;
297 	uintmax_t value;
298 	int rdback = mdb.m_flags & MDB_FL_READBACK;
299 	size_t i;
300 
301 	if (argc == 1) {
302 		mdb_warn("expected value to write following %c\n",
303 		    argv->a_un.a_char);
304 		return (DCMD_ERR);
305 	}
306 
307 	switch (argv->a_un.a_char) {
308 	case 'v':
309 		write_value = write_uint8;
310 		break;
311 	case 'w':
312 		write_value = write_uint16;
313 		break;
314 	case 'z':
315 		write_value = write_ctf_uint;
316 		break;
317 	case 'W':
318 		write_value = write_uint32;
319 		break;
320 	case 'Z':
321 		write_value = write_uint64;
322 		break;
323 	}
324 
325 	for (argv++, i = 1; i < argc; i++, argv++) {
326 		if (argv->a_type == MDB_TYPE_CHAR) {
327 			mdb_warn("expected immediate value instead of '%c'\n",
328 			    argv->a_un.a_char);
329 			return (DCMD_ERR);
330 		}
331 
332 		if (argv->a_type == MDB_TYPE_STRING) {
333 			if (mdb_eval(argv->a_un.a_str) == -1) {
334 				mdb_warn("failed to write \"%s\"",
335 				    argv->a_un.a_str);
336 				return (DCMD_ERR);
337 			}
338 			value = mdb_nv_get_value(mdb.m_dot);
339 		} else
340 			value = argv->a_un.a_val;
341 
342 		mdb_nv_set_value(mdb.m_dot, addr);
343 
344 		if ((naddr = write_value(as, addr, value, rdback)) == addr) {
345 			mdb_warn("failed to write %llr at address 0x%llx",
346 			    value, addr);
347 			mdb.m_incr = 0;
348 			return (DCMD_ERR);
349 		}
350 
351 		mdb.m_incr = naddr - addr;
352 		addr = naddr;
353 	}
354 
355 	return (DCMD_OK);
356 }
357 
358 static mdb_tgt_addr_t
359 match_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
360 {
361 	uint16_t x, val = (uint16_t)v64, mask = (uint16_t)m64;
362 
363 	for (; mdb_tgt_aread(mdb.m_target, as, &x,
364 	    sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
365 
366 		if ((x & mask) == val) {
367 			mdb_iob_printf(mdb.m_out, "%lla\n", addr);
368 			break;
369 		}
370 	}
371 	return (addr);
372 }
373 
374 static mdb_tgt_addr_t
375 match_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
376 {
377 	uint32_t x, val = (uint32_t)v64, mask = (uint32_t)m64;
378 
379 	for (; mdb_tgt_aread(mdb.m_target, as, &x,
380 	    sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
381 
382 		if ((x & mask) == val) {
383 			mdb_iob_printf(mdb.m_out, "%lla\n", addr);
384 			break;
385 		}
386 	}
387 	return (addr);
388 }
389 
390 static mdb_tgt_addr_t
391 match_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, uint64_t mask)
392 {
393 	uint64_t x;
394 
395 	for (; mdb_tgt_aread(mdb.m_target, as, &x,
396 	    sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
397 
398 		if ((x & mask) == val) {
399 			mdb_iob_printf(mdb.m_out, "%lla\n", addr);
400 			break;
401 		}
402 	}
403 	return (addr);
404 }
405 
406 static int
407 match_arglist(mdb_tgt_as_t as, uint_t flags, mdb_tgt_addr_t addr,
408     int argc, const mdb_arg_t *argv)
409 {
410 	mdb_tgt_addr_t (*match_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
411 	    uint64_t, uint64_t);
412 
413 	uint64_t args[2] = { 0, -1ULL }; /* [ value, mask ] */
414 	size_t i;
415 
416 	if (argc < 2) {
417 		mdb_warn("expected value following %c\n", argv->a_un.a_char);
418 		return (DCMD_ERR);
419 	}
420 
421 	if (argc > 3) {
422 		mdb_warn("only value and mask may follow %c\n",
423 		    argv->a_un.a_char);
424 		return (DCMD_ERR);
425 	}
426 
427 	switch (argv->a_un.a_char) {
428 	case 'l':
429 		match_value = match_uint16;
430 		break;
431 	case 'L':
432 		match_value = match_uint32;
433 		break;
434 	case 'M':
435 		match_value = match_uint64;
436 		break;
437 	}
438 
439 	for (argv++, i = 1; i < argc; i++, argv++) {
440 		if (argv->a_type == MDB_TYPE_CHAR) {
441 			mdb_warn("expected immediate value instead of '%c'\n",
442 			    argv->a_un.a_char);
443 			return (DCMD_ERR);
444 		}
445 
446 		if (argv->a_type == MDB_TYPE_STRING) {
447 			if (mdb_eval(argv->a_un.a_str) == -1) {
448 				mdb_warn("failed to evaluate \"%s\"",
449 				    argv->a_un.a_str);
450 				return (DCMD_ERR);
451 			}
452 			args[i - 1] = mdb_nv_get_value(mdb.m_dot);
453 		} else
454 			args[i - 1] = argv->a_un.a_val;
455 	}
456 
457 	addr = match_value(as, addr, args[0], args[1]);
458 	mdb_nv_set_value(mdb.m_dot, addr);
459 
460 	/*
461 	 * In adb(1), the match operators ignore any repeat count that has
462 	 * been applied to them.  We emulate this undocumented property
463 	 * by returning DCMD_ABORT if our input is not a pipeline.
464 	 */
465 	return ((flags & DCMD_PIPE) ? DCMD_OK : DCMD_ABORT);
466 }
467 
468 static int
469 argncmp(int argc, const mdb_arg_t *argv, const char *s)
470 {
471 	for (; *s != '\0'; s++, argc--, argv++) {
472 		if (argc == 0 || argv->a_type != MDB_TYPE_CHAR)
473 			return (FALSE);
474 		if (argv->a_un.a_char != *s)
475 			return (FALSE);
476 	}
477 	return (TRUE);
478 }
479 
480 static int
481 print_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint_t flags,
482     int argc, const mdb_arg_t *argv)
483 {
484 	char buf[MDB_TGT_SYM_NAMLEN];
485 	mdb_tgt_addr_t oaddr = addr;
486 	mdb_tgt_addr_t naddr;
487 	GElf_Sym sym;
488 	size_t i, n;
489 
490 	if (DCMD_HDRSPEC(flags) && (flags & DCMD_PIPE_OUT) == 0) {
491 		const char *fmt;
492 		int is_dis;
493 		/*
494 		 * This is nasty, but necessary for precise adb compatibility.
495 		 * Detect disassembly format by looking for "ai" or "ia":
496 		 */
497 		if (argncmp(argc, argv, "ai")) {
498 			fmt = "%-#*lla\n";
499 			is_dis = TRUE;
500 		} else if (argncmp(argc, argv, "ia")) {
501 			fmt = "%-#*lla";
502 			is_dis = TRUE;
503 		} else {
504 			fmt = "%-#*lla%16T";
505 			is_dis = FALSE;
506 		}
507 
508 		/*
509 		 * If symbolic decoding is on, disassembly is off, and the
510 		 * address exactly matches a symbol, print the symbol name:
511 		 */
512 		if ((mdb.m_flags & MDB_FL_PSYM) && !is_dis &&
513 		    (as == MDB_TGT_AS_VIRT || as == MDB_TGT_AS_FILE) &&
514 		    mdb_tgt_lookup_by_addr(mdb.m_target, (uintptr_t)addr,
515 		    MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0)
516 			mdb_iob_printf(mdb.m_out, "%s:\n", buf);
517 
518 		/*
519 		 * If this is a virtual address, cast it so that it reflects
520 		 * only the valid component of the address.
521 		 */
522 		if (as == MDB_TGT_AS_VIRT)
523 			addr = (uintptr_t)addr;
524 
525 		mdb_iob_printf(mdb.m_out, fmt,
526 		    (uint_t)mdb_iob_getmargin(mdb.m_out), addr);
527 	}
528 
529 	if (argc == 0) {
530 		/*
531 		 * Yes, for you trivia buffs: if you use a format verb and give
532 		 * no format string, you get: X^"= "i ... note that in adb the
533 		 * the '=' verb once had 'z' as its default, but then 'z' was
534 		 * deleted (it was once an alias for 'i') and so =\n now calls
535 		 * scanform("z") and produces a 'bad modifier' message.
536 		 */
537 		static const mdb_arg_t def_argv[] = {
538 			{ MDB_TYPE_CHAR, MDB_INIT_CHAR('X') },
539 			{ MDB_TYPE_CHAR, MDB_INIT_CHAR('^') },
540 			{ MDB_TYPE_STRING, MDB_INIT_STRING("= ") },
541 			{ MDB_TYPE_CHAR, MDB_INIT_CHAR('i') }
542 		};
543 
544 		argc = sizeof (def_argv) / sizeof (mdb_arg_t);
545 		argv = def_argv;
546 	}
547 
548 	mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
549 
550 	for (i = 0, n = 1; i < argc; i++, argv++) {
551 		switch (argv->a_type) {
552 		case MDB_TYPE_CHAR:
553 			naddr = mdb_fmt_print(mdb.m_target, as, addr, n,
554 			    argv->a_un.a_char);
555 			mdb.m_incr = naddr - addr;
556 			addr = naddr;
557 			n = 1;
558 			break;
559 
560 		case MDB_TYPE_IMMEDIATE:
561 			n = argv->a_un.a_val;
562 			break;
563 
564 		case MDB_TYPE_STRING:
565 			mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
566 			n = 1;
567 			break;
568 		}
569 	}
570 
571 	mdb.m_incr = addr - oaddr;
572 	mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
573 	return (DCMD_OK);
574 }
575 
576 static int
577 print_common(mdb_tgt_as_t as, uint_t flags, int argc, const mdb_arg_t *argv)
578 {
579 	mdb_tgt_addr_t addr = mdb_nv_get_value(mdb.m_dot);
580 
581 	if (argc != 0 && argv->a_type == MDB_TYPE_CHAR) {
582 		if (strchr("vwzWZ", argv->a_un.a_char))
583 			return (write_arglist(as, addr, argc, argv));
584 		if (strchr("lLM", argv->a_un.a_char))
585 			return (match_arglist(as, flags, addr, argc, argv));
586 	}
587 
588 	return (print_arglist(as, addr, flags, argc, argv));
589 }
590 
591 /*ARGSUSED*/
592 static int
593 cmd_print_core(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
594 {
595 	return (print_common(MDB_TGT_AS_VIRT, flags, argc, argv));
596 }
597 
598 #ifndef _KMDB
599 /*ARGSUSED*/
600 static int
601 cmd_print_object(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
602 {
603 	return (print_common(MDB_TGT_AS_FILE, flags, argc, argv));
604 }
605 #endif
606 
607 /*ARGSUSED*/
608 static int
609 cmd_print_phys(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
610 {
611 	return (print_common(MDB_TGT_AS_PHYS, flags, argc, argv));
612 }
613 
614 /*ARGSUSED*/
615 static int
616 cmd_print_value(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
617 {
618 	uintmax_t ndot, dot = mdb_get_dot();
619 	const char *tgt_argv[1];
620 	mdb_tgt_t *t;
621 	size_t i, n;
622 
623 	if (argc == 0) {
624 		mdb_warn("expected one or more format characters "
625 		    "following '='\n");
626 		return (DCMD_ERR);
627 	}
628 
629 	tgt_argv[0] = (const char *)&dot;
630 	t = mdb_tgt_create(mdb_value_tgt_create, 0, 1, tgt_argv);
631 	mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
632 
633 	for (i = 0, n = 1; i < argc; i++, argv++) {
634 		switch (argv->a_type) {
635 		case MDB_TYPE_CHAR:
636 			ndot = mdb_fmt_print(t, MDB_TGT_AS_VIRT,
637 			    dot, n, argv->a_un.a_char);
638 			if (argv->a_un.a_char == '+' ||
639 			    argv->a_un.a_char == '-')
640 				dot = ndot;
641 			n = 1;
642 			break;
643 
644 		case MDB_TYPE_IMMEDIATE:
645 			n = argv->a_un.a_val;
646 			break;
647 
648 		case MDB_TYPE_STRING:
649 			mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
650 			n = 1;
651 			break;
652 		}
653 	}
654 
655 	mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
656 	mdb_nv_set_value(mdb.m_dot, dot);
657 	mdb.m_incr = 0;
658 
659 	mdb_tgt_destroy(t);
660 	return (DCMD_OK);
661 }
662 
663 /*ARGSUSED*/
664 static int
665 cmd_assign_variable(uintptr_t addr, uint_t flags,
666     int argc, const mdb_arg_t *argv)
667 {
668 	uintmax_t dot = mdb_nv_get_value(mdb.m_dot);
669 	const char *p;
670 	mdb_var_t *v;
671 
672 	if (argc == 2) {
673 		if (argv->a_type != MDB_TYPE_CHAR) {
674 			mdb_warn("improper arguments following '>' operator\n");
675 			return (DCMD_ERR);
676 		}
677 
678 		switch (argv->a_un.a_char) {
679 		case 'c':
680 			addr = *((uchar_t *)&addr);
681 			break;
682 		case 's':
683 			addr = *((ushort_t *)&addr);
684 			break;
685 		case 'i':
686 			addr = *((uint_t *)&addr);
687 			break;
688 		case 'l':
689 			addr = *((ulong_t *)&addr);
690 			break;
691 		default:
692 			mdb_warn("%c is not a valid // modifier\n",
693 			    argv->a_un.a_char);
694 			return (DCMD_ERR);
695 		}
696 
697 		dot = addr;
698 		argv++;
699 		argc--;
700 	}
701 
702 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING) {
703 		mdb_warn("expected single variable name following '>'\n");
704 		return (DCMD_ERR);
705 	}
706 
707 	if (strlen(argv->a_un.a_str) >= (size_t)MDB_NV_NAMELEN) {
708 		mdb_warn("variable names may not exceed %d characters\n",
709 		    MDB_NV_NAMELEN - 1);
710 		return (DCMD_ERR);
711 	}
712 
713 	if ((p = strbadid(argv->a_un.a_str)) != NULL) {
714 		mdb_warn("'%c' may not be used in a variable name\n", *p);
715 		return (DCMD_ERR);
716 	}
717 
718 	if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
719 		(void) mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str, NULL, dot, 0);
720 	else
721 		mdb_nv_set_value(v, dot);
722 
723 	mdb.m_incr = 0;
724 	return (DCMD_OK);
725 }
726 
727 static int
728 print_soutype(const char *sou, uintptr_t addr, uint_t flags)
729 {
730 	static const char *prefixes[] = { "struct ", "union " };
731 	size_t namesz = 7 + strlen(sou) + 1;
732 	char *name = mdb_alloc(namesz, UM_SLEEP | UM_GC);
733 	mdb_ctf_id_t id;
734 	int i;
735 
736 	for (i = 0; i < 2; i++) {
737 		(void) mdb_snprintf(name, namesz, "%s%s", prefixes[i], sou);
738 
739 		if (mdb_ctf_lookup_by_name(name, &id) == 0) {
740 			mdb_arg_t v;
741 			int rv;
742 
743 			v.a_type = MDB_TYPE_STRING;
744 			v.a_un.a_str = name;
745 
746 			rv = mdb_call_dcmd("print", addr, flags, 1, &v);
747 			return (rv);
748 		}
749 	}
750 
751 	return (DCMD_ERR);
752 }
753 
754 static int
755 print_type(const char *name, uintptr_t addr, uint_t flags)
756 {
757 	mdb_ctf_id_t id;
758 	char *sname;
759 	size_t snamesz;
760 	int rv;
761 
762 	if (!(flags & DCMD_ADDRSPEC)) {
763 		addr = mdb_get_dot();
764 		flags |= DCMD_ADDRSPEC;
765 	}
766 
767 	if ((rv = print_soutype(name, addr, flags)) != DCMD_ERR)
768 		return (rv);
769 
770 	snamesz = strlen(name) + 3;
771 	sname = mdb_zalloc(snamesz, UM_SLEEP | UM_GC);
772 	(void) mdb_snprintf(sname, snamesz, "%s_t", name);
773 
774 	if (mdb_ctf_lookup_by_name(sname, &id) == 0) {
775 		mdb_arg_t v;
776 		int rv;
777 
778 		v.a_type = MDB_TYPE_STRING;
779 		v.a_un.a_str = sname;
780 
781 		rv = mdb_call_dcmd("print", addr, flags, 1, &v);
782 		return (rv);
783 	}
784 
785 	sname[snamesz - 2] = 's';
786 	rv = print_soutype(sname, addr, flags);
787 	return (rv);
788 }
789 
790 static int
791 exec_alias(const char *fname, uintptr_t addr, uint_t flags)
792 {
793 	const char *alias;
794 	int rv;
795 
796 	if ((alias = mdb_macalias_lookup(fname)) == NULL)
797 		return (DCMD_ERR);
798 
799 	if (flags & DCMD_ADDRSPEC) {
800 		size_t sz = sizeof (uintptr_t) * 2 + strlen(alias) + 1;
801 		char *addralias = mdb_alloc(sz, UM_SLEEP | UM_GC);
802 		(void) mdb_snprintf(addralias, sz, "%p%s", addr, alias);
803 		rv = mdb_eval(addralias);
804 	} else {
805 		rv = mdb_eval(alias);
806 	}
807 
808 	return (rv == -1 ? DCMD_ABORT : DCMD_OK);
809 }
810 
811 /*ARGSUSED*/
812 static int
813 cmd_src_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
814 {
815 	const char *fname;
816 	mdb_io_t *fio;
817 	int rv;
818 
819 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
820 		return (DCMD_USAGE);
821 
822 	fname = argv->a_un.a_str;
823 
824 	if (flags & DCMD_PIPE_OUT) {
825 		mdb_warn("macro files cannot be used as input to a pipeline\n");
826 		return (DCMD_ABORT);
827 	}
828 
829 	if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
830 	    O_RDONLY, 0)) != NULL) {
831 		mdb_frame_t *fp = mdb.m_frame;
832 		int err;
833 
834 		mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
835 		mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
836 		err = mdb_run();
837 
838 		ASSERT(fp == mdb.m_frame);
839 		mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
840 		yylineno = mdb_iob_lineno(mdb.m_in);
841 
842 		if (err == MDB_ERR_PAGER && mdb.m_fmark != fp)
843 			longjmp(fp->f_pcb, err);
844 
845 		if (err == MDB_ERR_QUIT || err == MDB_ERR_ABORT ||
846 		    err == MDB_ERR_SIGINT || err == MDB_ERR_OUTPUT)
847 			longjmp(fp->f_pcb, err);
848 
849 		return (DCMD_OK);
850 	}
851 
852 	if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
853 	    (rv = print_type(fname, addr, flags)) != DCMD_ERR)
854 		return (rv);
855 
856 	mdb_warn("failed to open %s (see ::help '$<')\n", fname);
857 	return (DCMD_ABORT);
858 }
859 
860 static int
861 cmd_exec_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
862 {
863 	const char *fname;
864 	mdb_io_t *fio;
865 	int rv;
866 
867 	/*
868 	 * The syntax [expr[,count]]$< with no trailing macro file name is
869 	 * magic in that if count is zero, this command won't be called and
870 	 * the expression is thus a no-op.  If count is non-zero, we get
871 	 * invoked with argc == 0, and this means abort the current macro.
872 	 * If our debugger stack depth is greater than one, we may be using
873 	 * $< from within a previous $<<, so in that case we set m_in to
874 	 * NULL to force this entire frame to be popped.
875 	 */
876 	if (argc == 0) {
877 		if (mdb_iob_stack_size(&mdb.m_frame->f_istk) != 0) {
878 			mdb_iob_destroy(mdb.m_in);
879 			mdb.m_in = mdb_iob_stack_pop(&mdb.m_frame->f_istk);
880 		} else if (mdb.m_depth > 1) {
881 			mdb_iob_destroy(mdb.m_in);
882 			mdb.m_in = NULL;
883 		} else
884 			mdb_warn("input stack is empty\n");
885 		return (DCMD_OK);
886 	}
887 
888 	if ((flags & (DCMD_PIPE | DCMD_PIPE_OUT)) || mdb.m_depth == 1)
889 		return (cmd_src_file(addr, flags, argc, argv));
890 
891 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
892 		return (DCMD_USAGE);
893 
894 	fname = argv->a_un.a_str;
895 
896 	if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
897 	    O_RDONLY, 0)) != NULL) {
898 		mdb_iob_destroy(mdb.m_in);
899 		mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
900 		return (DCMD_OK);
901 	}
902 
903 	if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
904 	    (rv = print_type(fname, addr, flags)) != DCMD_ERR)
905 		return (rv);
906 
907 	mdb_warn("failed to open %s (see ::help '$<')\n", fname);
908 	return (DCMD_ABORT);
909 }
910 
911 #ifndef _KMDB
912 /*ARGSUSED*/
913 static int
914 cmd_cat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
915 {
916 	int status = DCMD_OK;
917 	char buf[BUFSIZ];
918 	mdb_iob_t *iob;
919 	mdb_io_t *fio;
920 
921 	if (flags & DCMD_ADDRSPEC)
922 		return (DCMD_USAGE);
923 
924 	for (; argc-- != 0; argv++) {
925 		if (argv->a_type != MDB_TYPE_STRING) {
926 			mdb_warn("expected string argument\n");
927 			status = DCMD_ERR;
928 			continue;
929 		}
930 
931 		if ((fio = mdb_fdio_create_path(NULL,
932 		    argv->a_un.a_str, O_RDONLY, 0)) == NULL) {
933 			mdb_warn("failed to open %s", argv->a_un.a_str);
934 			status = DCMD_ERR;
935 			continue;
936 		}
937 
938 		iob = mdb_iob_create(fio, MDB_IOB_RDONLY);
939 
940 		while (!(mdb_iob_getflags(iob) & (MDB_IOB_EOF | MDB_IOB_ERR))) {
941 			ssize_t len = mdb_iob_read(iob, buf, sizeof (buf));
942 			if (len > 0) {
943 				if (mdb_iob_write(mdb.m_out, buf, len) < 0) {
944 					if (errno != EPIPE)
945 						mdb_warn("write failed");
946 					status = DCMD_ERR;
947 					break;
948 				}
949 			}
950 		}
951 
952 		if (mdb_iob_err(iob))
953 			mdb_warn("error while reading %s", mdb_iob_name(iob));
954 
955 		mdb_iob_destroy(iob);
956 	}
957 
958 	return (status);
959 }
960 #endif
961 
962 /*ARGSUSED*/
963 static int
964 cmd_grep(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
965 {
966 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
967 		return (DCMD_USAGE);
968 
969 	if (mdb_eval(argv->a_un.a_str) == -1)
970 		return (DCMD_ABORT);
971 
972 	if (mdb_get_dot() != 0)
973 		mdb_printf("%lr\n", addr);
974 
975 	return (DCMD_OK);
976 }
977 
978 /*ARGSUSED*/
979 static int
980 cmd_map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
981 {
982 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
983 		return (DCMD_USAGE);
984 
985 	if (mdb_eval(argv->a_un.a_str) == -1)
986 		return (DCMD_ABORT);
987 
988 	mdb_printf("%llr\n", mdb_get_dot());
989 	return (DCMD_OK);
990 }
991 
992 /*ARGSUSED*/
993 static int
994 cmd_notsup(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
995 {
996 	mdb_warn("command is not supported by current target\n");
997 	return (DCMD_ERR);
998 }
999 
1000 /*ARGSUSED*/
1001 static int
1002 cmd_quit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1003 {
1004 #ifdef _KMDB
1005 	uint_t opt_u = FALSE;
1006 
1007 	if (mdb_getopts(argc, argv,
1008 	    'u', MDB_OPT_SETBITS, TRUE, &opt_u, NULL) != argc)
1009 		return (DCMD_USAGE);
1010 
1011 	if (opt_u) {
1012 		if (mdb.m_flags & MDB_FL_NOUNLOAD) {
1013 			warn("%s\n", mdb_strerror(EMDB_KNOUNLOAD));
1014 			return (DCMD_ERR);
1015 		}
1016 
1017 		kmdb_kdi_set_unload_request();
1018 	}
1019 #endif
1020 
1021 	longjmp(mdb.m_frame->f_pcb, MDB_ERR_QUIT);
1022 	/*NOTREACHED*/
1023 	return (DCMD_ERR);
1024 }
1025 
1026 #ifdef _KMDB
1027 static void
1028 quit_help(void)
1029 {
1030 	mdb_printf(
1031 	    "-u    unload the debugger (if not loaded at boot)\n");
1032 }
1033 #endif
1034 
1035 /*ARGSUSED*/
1036 static int
1037 cmd_vars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1038 {
1039 	uint_t opt_nz = FALSE, opt_tag = FALSE, opt_prt = FALSE;
1040 	mdb_var_t *v;
1041 
1042 	if (mdb_getopts(argc, argv,
1043 	    'n', MDB_OPT_SETBITS, TRUE, &opt_nz,
1044 	    'p', MDB_OPT_SETBITS, TRUE, &opt_prt,
1045 	    't', MDB_OPT_SETBITS, TRUE, &opt_tag, NULL) != argc)
1046 		return (DCMD_USAGE);
1047 
1048 	mdb_nv_rewind(&mdb.m_nv);
1049 
1050 	while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
1051 		if ((opt_tag == FALSE || (v->v_flags & MDB_NV_TAGGED)) &&
1052 		    (opt_nz == FALSE || mdb_nv_get_value(v) != 0)) {
1053 			if (opt_prt) {
1054 				mdb_printf("%#llr>%s\n",
1055 				    mdb_nv_get_value(v), mdb_nv_get_name(v));
1056 			} else {
1057 				mdb_printf("%s = %llr\n",
1058 				    mdb_nv_get_name(v), mdb_nv_get_value(v));
1059 			}
1060 		}
1061 	}
1062 
1063 	return (DCMD_OK);
1064 }
1065 
1066 /*ARGSUSED*/
1067 static int
1068 cmd_nzvars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1069 {
1070 	uintmax_t value;
1071 	mdb_var_t *v;
1072 
1073 	if (argc != 0)
1074 		return (DCMD_USAGE);
1075 
1076 	mdb_nv_rewind(&mdb.m_nv);
1077 
1078 	while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
1079 		if ((value = mdb_nv_get_value(v)) != 0)
1080 			mdb_printf("%s = %llr\n", mdb_nv_get_name(v), value);
1081 	}
1082 
1083 	return (DCMD_OK);
1084 }
1085 
1086 /*ARGSUSED*/
1087 static int
1088 cmd_radix(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1089 {
1090 	if (argc != 0)
1091 		return (DCMD_USAGE);
1092 
1093 	if (flags & DCMD_ADDRSPEC) {
1094 		if (addr < 2 || addr > 16) {
1095 			mdb_warn("expected radix from 2 to 16\n");
1096 			return (DCMD_ERR);
1097 		}
1098 		mdb.m_radix = (int)addr;
1099 	}
1100 
1101 	mdb_iob_printf(mdb.m_out, "radix = %d base ten\n", mdb.m_radix);
1102 	return (DCMD_OK);
1103 }
1104 
1105 /*ARGSUSED*/
1106 static int
1107 cmd_symdist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1108 {
1109 	if (argc != 0)
1110 		return (DCMD_USAGE);
1111 
1112 	if (flags & DCMD_ADDRSPEC)
1113 		mdb.m_symdist = addr;
1114 
1115 	mdb_printf("symbol matching distance = %lr (%s)\n",
1116 	    mdb.m_symdist, mdb.m_symdist ? "absolute mode" : "smart mode");
1117 
1118 	return (DCMD_OK);
1119 }
1120 
1121 /*ARGSUSED*/
1122 static int
1123 cmd_pgwidth(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1124 {
1125 	if (argc != 0)
1126 		return (DCMD_USAGE);
1127 
1128 	if (flags & DCMD_ADDRSPEC)
1129 		mdb_iob_resize(mdb.m_out, mdb.m_out->iob_rows, addr);
1130 
1131 	mdb_printf("output page width = %lu\n", mdb.m_out->iob_cols);
1132 	return (DCMD_OK);
1133 }
1134 
1135 /*ARGSUSED*/
1136 static int
1137 cmd_reopen(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1138 {
1139 	if (argc != 0)
1140 		return (DCMD_USAGE);
1141 
1142 	if (mdb_tgt_setflags(mdb.m_target, MDB_TGT_F_RDWR) == -1) {
1143 		mdb_warn("failed to re-open target for writing");
1144 		return (DCMD_ERR);
1145 	}
1146 
1147 	return (DCMD_OK);
1148 }
1149 
1150 /*ARGSUSED*/
1151 static int
1152 print_xdata(void *ignored, const char *name, const char *desc, size_t nbytes)
1153 {
1154 	mdb_printf("%-24s - %s (%lu bytes)\n", name, desc, (ulong_t)nbytes);
1155 	return (0);
1156 }
1157 
1158 /*ARGSUSED*/
1159 static int
1160 cmd_xdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1161 {
1162 	if (argc != 0 || (flags & DCMD_ADDRSPEC))
1163 		return (DCMD_USAGE);
1164 
1165 	(void) mdb_tgt_xdata_iter(mdb.m_target, print_xdata, NULL);
1166 	return (DCMD_OK);
1167 }
1168 
1169 /*ARGSUSED*/
1170 static int
1171 cmd_unset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1172 {
1173 	mdb_var_t *v;
1174 	size_t i;
1175 
1176 	for (i = 0; i < argc; i++) {
1177 		if (argv[i].a_type != MDB_TYPE_STRING) {
1178 			mdb_warn("bad option: arg %lu is not a string\n",
1179 			    (ulong_t)i + 1);
1180 			return (DCMD_USAGE);
1181 		}
1182 	}
1183 
1184 	for (i = 0; i < argc; i++, argv++) {
1185 		if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
1186 			mdb_warn("variable '%s' not defined\n",
1187 			    argv->a_un.a_str);
1188 		else
1189 			mdb_nv_remove(&mdb.m_nv, v);
1190 	}
1191 
1192 	return (DCMD_OK);
1193 }
1194 
1195 #ifndef _KMDB
1196 /*ARGSUSED*/
1197 static int
1198 cmd_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1199 {
1200 	uint_t opt_e = FALSE, opt_d = FALSE;
1201 	const char *filename = NULL;
1202 	int i;
1203 
1204 	i = mdb_getopts(argc, argv,
1205 	    'd', MDB_OPT_SETBITS, TRUE, &opt_d,
1206 	    'e', MDB_OPT_SETBITS, TRUE, &opt_e, NULL);
1207 
1208 	if ((i != argc && i != argc - 1) || (opt_d && opt_e) ||
1209 	    (i != argc && argv[i].a_type != MDB_TYPE_STRING) ||
1210 	    (i != argc && opt_d == TRUE) || (flags & DCMD_ADDRSPEC))
1211 		return (DCMD_USAGE);
1212 
1213 	if (mdb.m_depth != 1) {
1214 		mdb_warn("log may not be manipulated in this context\n");
1215 		return (DCMD_ABORT);
1216 	}
1217 
1218 	if (i != argc)
1219 		filename = argv[i].a_un.a_str;
1220 
1221 	/*
1222 	 * If no arguments were specified, print the log file name (if any)
1223 	 * and report whether the log is enabled or disabled.
1224 	 */
1225 	if (argc == 0) {
1226 		if (mdb.m_log) {
1227 			mdb_printf("%s: logging to \"%s\" is currently %s\n",
1228 			    mdb.m_pname, IOP_NAME(mdb.m_log),
1229 			    mdb.m_flags & MDB_FL_LOG ?  "enabled" : "disabled");
1230 		} else
1231 			mdb_printf("%s: no log is active\n", mdb.m_pname);
1232 		return (DCMD_OK);
1233 	}
1234 
1235 	/*
1236 	 * If the -d option was specified, pop the log i/o object off the
1237 	 * i/o stack of stdin, stdout, and stderr.
1238 	 */
1239 	if (opt_d) {
1240 		if (mdb.m_flags & MDB_FL_LOG) {
1241 			(void) mdb_iob_pop_io(mdb.m_in);
1242 			(void) mdb_iob_pop_io(mdb.m_out);
1243 			(void) mdb_iob_pop_io(mdb.m_err);
1244 			mdb.m_flags &= ~MDB_FL_LOG;
1245 		} else
1246 			mdb_warn("logging is already disabled\n");
1247 		return (DCMD_OK);
1248 	}
1249 
1250 	/*
1251 	 * The -e option is the default: (re-)enable logging by pushing
1252 	 * the log i/o object on to stdin, stdout, and stderr.  If we have
1253 	 * a previous log file, we need to pop it and close it.  If we have
1254 	 * no new log file, push the previous one back on.
1255 	 */
1256 	if (filename != NULL) {
1257 		if (mdb.m_log != NULL) {
1258 			if (mdb.m_flags & MDB_FL_LOG) {
1259 				(void) mdb_iob_pop_io(mdb.m_in);
1260 				(void) mdb_iob_pop_io(mdb.m_out);
1261 				(void) mdb_iob_pop_io(mdb.m_err);
1262 				mdb.m_flags &= ~MDB_FL_LOG;
1263 			}
1264 			mdb_io_rele(mdb.m_log);
1265 		}
1266 
1267 		mdb.m_log = mdb_fdio_create_path(NULL, filename,
1268 		    O_CREAT | O_APPEND | O_WRONLY, 0666);
1269 
1270 		if (mdb.m_log == NULL) {
1271 			mdb_warn("failed to open %s", filename);
1272 			return (DCMD_ERR);
1273 		}
1274 	}
1275 
1276 	if (mdb.m_log != NULL) {
1277 		mdb_iob_push_io(mdb.m_in, mdb_logio_create(mdb.m_log));
1278 		mdb_iob_push_io(mdb.m_out, mdb_logio_create(mdb.m_log));
1279 		mdb_iob_push_io(mdb.m_err, mdb_logio_create(mdb.m_log));
1280 
1281 		mdb_printf("%s: logging to \"%s\"\n", mdb.m_pname, filename);
1282 		mdb.m_log = mdb_io_hold(mdb.m_log);
1283 		mdb.m_flags |= MDB_FL_LOG;
1284 
1285 		return (DCMD_OK);
1286 	}
1287 
1288 	mdb_warn("no log file has been selected\n");
1289 	return (DCMD_ERR);
1290 }
1291 
1292 static int
1293 cmd_old_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1294 {
1295 	if (argc == 0) {
1296 		mdb_arg_t arg = { MDB_TYPE_STRING, MDB_INIT_STRING("-d") };
1297 		return (cmd_log(addr, flags, 1, &arg));
1298 	}
1299 
1300 	return (cmd_log(addr, flags, argc, argv));
1301 }
1302 #endif
1303 
1304 /*ARGSUSED*/
1305 static int
1306 cmd_load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1307 {
1308 	int i, mode = MDB_MOD_LOCAL;
1309 
1310 	i = mdb_getopts(argc, argv,
1311 #ifdef _KMDB
1312 	    'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
1313 #endif
1314 	    'f', MDB_OPT_SETBITS, MDB_MOD_FORCE, &mode,
1315 	    'g', MDB_OPT_SETBITS, MDB_MOD_GLOBAL, &mode,
1316 	    's', MDB_OPT_SETBITS, MDB_MOD_SILENT, &mode,
1317 	    NULL);
1318 
1319 	argc -= i;
1320 	argv += i;
1321 
1322 	if ((flags & DCMD_ADDRSPEC) || argc != 1 ||
1323 	    argv->a_type != MDB_TYPE_STRING ||
1324 	    strchr("+-", argv->a_un.a_str[0]) != NULL)
1325 		return (DCMD_USAGE);
1326 
1327 	if (mdb_module_load(argv->a_un.a_str, mode) < 0)
1328 		return (DCMD_ERR);
1329 
1330 	return (DCMD_OK);
1331 }
1332 
1333 static void
1334 load_help(void)
1335 {
1336 	mdb_printf(
1337 #ifdef _KMDB
1338 	    "-d    defer load until next continue\n"
1339 #endif
1340 	    "-s    load module silently\n");
1341 }
1342 
1343 /*ARGSUSED*/
1344 static int
1345 cmd_unload(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1346 {
1347 	int mode = 0;
1348 	int i;
1349 
1350 	i = mdb_getopts(argc, argv,
1351 #ifdef _KMDB
1352 	    'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
1353 #endif
1354 	    NULL);
1355 
1356 	argc -= i;
1357 	argv += i;
1358 
1359 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
1360 		return (DCMD_USAGE);
1361 
1362 	if (mdb_module_unload(argv->a_un.a_str, mode) == -1) {
1363 		mdb_warn("failed to unload %s", argv->a_un.a_str);
1364 		return (DCMD_ERR);
1365 	}
1366 
1367 	return (DCMD_OK);
1368 }
1369 
1370 #ifdef _KMDB
1371 static void
1372 unload_help(void)
1373 {
1374 	mdb_printf(
1375 	    "-d    defer unload until next continue\n");
1376 }
1377 #endif
1378 
1379 static int
1380 cmd_dbmode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1381 {
1382 	if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
1383 		return (DCMD_USAGE);
1384 
1385 	if (argc != 0) {
1386 		if (argv->a_type != MDB_TYPE_STRING)
1387 			return (DCMD_USAGE);
1388 		if ((addr = mdb_dstr2mode(argv->a_un.a_str)) != MDB_DBG_HELP)
1389 			mdb_dmode(addr);
1390 	} else if (flags & DCMD_ADDRSPEC)
1391 		mdb_dmode(addr);
1392 
1393 	mdb_printf("debugging mode = 0x%04x\n", mdb.m_debug);
1394 	return (DCMD_OK);
1395 }
1396 
1397 /*ARGSUSED*/
1398 static int
1399 cmd_version(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1400 {
1401 #ifdef DEBUG
1402 	mdb_printf("\r%s (DEBUG)\n", mdb_conf_version());
1403 #else
1404 	mdb_printf("\r%s\n", mdb_conf_version());
1405 #endif
1406 	return (DCMD_OK);
1407 }
1408 
1409 /*ARGSUSED*/
1410 static int
1411 cmd_algol(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1412 {
1413 	if (mdb.m_flags & MDB_FL_ADB)
1414 		mdb_printf("No algol 68 here\n");
1415 	else
1416 		mdb_printf("No adb here\n");
1417 	return (DCMD_OK);
1418 }
1419 
1420 /*ARGSUSED*/
1421 static int
1422 cmd_obey(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1423 {
1424 	if (mdb.m_flags & MDB_FL_ADB)
1425 		mdb_printf("CHAPTER 1\n");
1426 	else
1427 		mdb_printf("No Language H here\n");
1428 	return (DCMD_OK);
1429 }
1430 
1431 /*ARGSUSED*/
1432 static int
1433 print_global(void *data, const GElf_Sym *sym, const char *name,
1434     const mdb_syminfo_t *sip, const char *obj)
1435 {
1436 	uintptr_t value;
1437 
1438 	if (mdb_tgt_vread((mdb_tgt_t *)data, &value, sizeof (value),
1439 	    (uintptr_t)sym->st_value) == sizeof (value))
1440 		mdb_printf("%s(%llr):\t%lr\n", name, sym->st_value, value);
1441 	else
1442 		mdb_printf("%s(%llr):\t?\n", name, sym->st_value);
1443 
1444 	return (0);
1445 }
1446 
1447 /*ARGSUSED*/
1448 static int
1449 cmd_globals(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1450 {
1451 	if (argc != 0)
1452 		return (DCMD_USAGE);
1453 
1454 	(void) mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
1455 	    MDB_TGT_SYMTAB, MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_OBJECT |
1456 	    MDB_TGT_TYPE_FUNC, print_global, mdb.m_target);
1457 
1458 	return (0);
1459 }
1460 
1461 /*ARGSUSED*/
1462 static int
1463 cmd_eval(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1464 {
1465 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
1466 		return (DCMD_USAGE);
1467 
1468 	if (mdb_eval(argv->a_un.a_str) == -1)
1469 		return (DCMD_ABORT);
1470 
1471 	return (DCMD_OK);
1472 }
1473 
1474 /*ARGSUSED*/
1475 static int
1476 print_file(void *data, const GElf_Sym *sym, const char *name,
1477     const mdb_syminfo_t *sip, const char *obj)
1478 {
1479 	int i = *((int *)data);
1480 
1481 	mdb_printf("%d\t%s\n", i++, name);
1482 	*((int *)data) = i;
1483 	return (0);
1484 }
1485 
1486 /*ARGSUSED*/
1487 static int
1488 cmd_files(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1489 {
1490 	int i = 1;
1491 	const char *obj = MDB_TGT_OBJ_EVERY;
1492 
1493 	if ((flags & DCMD_ADDRSPEC) || argc > 1)
1494 		return (DCMD_USAGE);
1495 
1496 	if (argc == 1) {
1497 		if (argv->a_type != MDB_TYPE_STRING)
1498 			return (DCMD_USAGE);
1499 
1500 		obj = argv->a_un.a_str;
1501 	}
1502 
1503 	(void) mdb_tgt_symbol_iter(mdb.m_target, obj, MDB_TGT_SYMTAB,
1504 	    MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FILE, print_file, &i);
1505 
1506 	return (DCMD_OK);
1507 }
1508 
1509 static const char *
1510 map_name(const mdb_map_t *map, const char *name)
1511 {
1512 	if (map->map_flags & MDB_TGT_MAP_HEAP)
1513 		return ("[ heap ]");
1514 	if (name != NULL && name[0] != 0)
1515 		return (name);
1516 
1517 	if (map->map_flags & MDB_TGT_MAP_SHMEM)
1518 		return ("[ shmem ]");
1519 	if (map->map_flags & MDB_TGT_MAP_STACK)
1520 		return ("[ stack ]");
1521 	if (map->map_flags & MDB_TGT_MAP_ANON)
1522 		return ("[ anon ]");
1523 	if (map->map_name != NULL)
1524 		return (map->map_name);
1525 	return ("[ unknown ]");
1526 }
1527 
1528 /*ARGSUSED*/
1529 static int
1530 print_map(void *ignored, const mdb_map_t *map, const char *name)
1531 {
1532 	name = map_name(map, name);
1533 
1534 	mdb_printf("%?p %?p %?lx %s\n", map->map_base,
1535 	    map->map_base + map->map_size, map->map_size, name);
1536 	return (0);
1537 }
1538 
1539 static int
1540 cmd_mappings(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1541 {
1542 	const mdb_map_t *m;
1543 
1544 	if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
1545 		return (DCMD_USAGE);
1546 
1547 	mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
1548 	    "BASE", "LIMIT", "SIZE", "NAME");
1549 
1550 	if (flags & DCMD_ADDRSPEC) {
1551 		if ((m = mdb_tgt_addr_to_map(mdb.m_target, addr)) == NULL)
1552 			mdb_warn("failed to obtain mapping");
1553 		else
1554 			(void) print_map(NULL, m, NULL);
1555 
1556 	} else if (argc != 0) {
1557 		if (argv->a_type == MDB_TYPE_STRING)
1558 			m = mdb_tgt_name_to_map(mdb.m_target, argv->a_un.a_str);
1559 		else
1560 			m = mdb_tgt_addr_to_map(mdb.m_target, argv->a_un.a_val);
1561 
1562 		if (m == NULL)
1563 			mdb_warn("failed to obtain mapping");
1564 		else
1565 			(void) print_map(NULL, m, NULL);
1566 
1567 	} else if (mdb_tgt_mapping_iter(mdb.m_target, print_map, NULL) == -1)
1568 		mdb_warn("failed to iterate over mappings");
1569 
1570 	return (DCMD_OK);
1571 }
1572 
1573 static int
1574 whatis_map_callback(void *wp, const mdb_map_t *map, const char *name)
1575 {
1576 	mdb_whatis_t *w = wp;
1577 	uintptr_t cur;
1578 
1579 	name = map_name(map, name);
1580 
1581 	while (mdb_whatis_match(w, map->map_base, map->map_size, &cur))
1582 		mdb_whatis_report_address(w, cur, "in %s [%p,%p)\n",
1583 		    name, map->map_base, map->map_base + map->map_size);
1584 
1585 	return (0);
1586 }
1587 
1588 /*ARGSUSED*/
1589 int
1590 whatis_run_mappings(mdb_whatis_t *w, void *ignored)
1591 {
1592 	(void) mdb_tgt_mapping_iter(mdb.m_target, whatis_map_callback, w);
1593 	return (0);
1594 }
1595 
1596 /*ARGSUSED*/
1597 static int
1598 objects_printversion(void *ignored, const mdb_map_t *map, const char *name)
1599 {
1600 	ctf_file_t *ctfp;
1601 	const char *version;
1602 
1603 	ctfp = mdb_tgt_name_to_ctf(mdb.m_target, name);
1604 	if (ctfp == NULL || (version = ctf_label_topmost(ctfp)) == NULL)
1605 		version = "Unknown";
1606 
1607 	mdb_printf("%-28s %s\n", name, version);
1608 	return (0);
1609 }
1610 
1611 /*ARGSUSED*/
1612 static int
1613 cmd_objects(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1614 {
1615 	uint_t opt_v = FALSE;
1616 	mdb_tgt_map_f *cb;
1617 
1618 	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
1619 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
1620 		return (DCMD_USAGE);
1621 
1622 	if (opt_v) {
1623 		cb = objects_printversion;
1624 		mdb_printf("%<u>%-28s %s%</u>\n", "NAME", "VERSION");
1625 	} else {
1626 		cb = print_map;
1627 		mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
1628 		    "BASE", "LIMIT", "SIZE", "NAME");
1629 	}
1630 
1631 	if (mdb_tgt_object_iter(mdb.m_target, cb, NULL) == -1) {
1632 		mdb_warn("failed to iterate over objects");
1633 		return (DCMD_ERR);
1634 	}
1635 
1636 	return (DCMD_OK);
1637 }
1638 
1639 /*ARGSUSED*/
1640 static int
1641 showrev_addversion(void *vers_nv, const mdb_map_t *ignored, const char *object)
1642 {
1643 	ctf_file_t *ctfp;
1644 	const char *version = NULL;
1645 	char *objname;
1646 
1647 	objname = mdb_alloc(strlen(object) + 1, UM_SLEEP | UM_GC);
1648 	(void) strcpy(objname, object);
1649 
1650 	if ((ctfp = mdb_tgt_name_to_ctf(mdb.m_target, objname)) != NULL)
1651 		version = ctf_label_topmost(ctfp);
1652 
1653 	/*
1654 	 * Not all objects have CTF and label data, so set version to "Unknown".
1655 	 */
1656 	if (version == NULL)
1657 		version = "Unknown";
1658 
1659 	(void) mdb_nv_insert(vers_nv, version, NULL, (uintptr_t)objname,
1660 	    MDB_NV_OVERLOAD);
1661 
1662 	return (0);
1663 }
1664 
1665 static int
1666 showrev_ispatch(const char *s)
1667 {
1668 	if (s == NULL)
1669 		return (0);
1670 
1671 	if (*s == 'T')
1672 		s++; /* skip T for T-patch */
1673 
1674 	for (; *s != '\0'; s++) {
1675 		if ((*s < '0' || *s > '9') && *s != '-')
1676 			return (0);
1677 	}
1678 
1679 	return (1);
1680 }
1681 
1682 /*ARGSUSED*/
1683 static int
1684 showrev_printobject(mdb_var_t *v, void *ignored)
1685 {
1686 	mdb_printf("%s ", MDB_NV_COOKIE(v));
1687 	return (0);
1688 }
1689 
1690 static int
1691 showrev_printversion(mdb_var_t *v, void *showall)
1692 {
1693 	const char *version = mdb_nv_get_name(v);
1694 	int patch;
1695 
1696 	patch = showrev_ispatch(version);
1697 	if (patch || (uintptr_t)showall) {
1698 		mdb_printf("%s: %s  Objects: ",
1699 		    (patch ? "Patch" : "Version"), version);
1700 		(void) mdb_inc_indent(2);
1701 
1702 		mdb_nv_defn_iter(v, showrev_printobject, NULL);
1703 
1704 		(void) mdb_dec_indent(2);
1705 		mdb_printf("\n");
1706 	}
1707 
1708 	return (0);
1709 }
1710 
1711 /*
1712  * Display version information for each object in the system.
1713  * Print information about patches only, unless showall is TRUE.
1714  */
1715 static int
1716 showrev_objectversions(int showall)
1717 {
1718 	mdb_nv_t vers_nv;
1719 
1720 	(void) mdb_nv_create(&vers_nv, UM_SLEEP | UM_GC);
1721 	if (mdb_tgt_object_iter(mdb.m_target, showrev_addversion,
1722 	    &vers_nv) == -1) {
1723 		mdb_warn("failed to iterate over objects");
1724 		return (DCMD_ERR);
1725 	}
1726 
1727 	mdb_nv_sort_iter(&vers_nv, showrev_printversion,
1728 	    (void *)(uintptr_t)showall, UM_SLEEP | UM_GC);
1729 	return (DCMD_OK);
1730 }
1731 
1732 /*
1733  * Display information similar to what showrev(1M) displays when invoked
1734  * with no arguments.
1735  */
1736 static int
1737 showrev_sysinfo(void)
1738 {
1739 	const char *s;
1740 	int rc;
1741 	struct utsname u;
1742 
1743 	if ((rc = mdb_tgt_uname(mdb.m_target, &u)) != -1) {
1744 		mdb_printf("Hostname: %s\n", u.nodename);
1745 		mdb_printf("Release: %s\n", u.release);
1746 		mdb_printf("Kernel architecture: %s\n", u.machine);
1747 	}
1748 
1749 	/*
1750 	 * Match the order of the showrev(1M) output and put "Application
1751 	 * architecture" before "Kernel version"
1752 	 */
1753 	if ((s = mdb_tgt_isa(mdb.m_target)) != NULL)
1754 		mdb_printf("Application architecture: %s\n", s);
1755 
1756 	if (rc != -1)
1757 		mdb_printf("Kernel version: %s %s %s %s\n",
1758 		    u.sysname, u.release, u.machine, u.version);
1759 
1760 	if ((s = mdb_tgt_platform(mdb.m_target)) != NULL)
1761 		mdb_printf("Platform: %s\n", s);
1762 
1763 	return (DCMD_OK);
1764 }
1765 
1766 /*ARGSUSED*/
1767 static int
1768 cmd_showrev(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1769 {
1770 	uint_t opt_p = FALSE, opt_v = FALSE;
1771 
1772 	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
1773 	    'p', MDB_OPT_SETBITS, TRUE, &opt_p,
1774 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
1775 		return (DCMD_USAGE);
1776 
1777 	if (opt_p || opt_v)
1778 		return (showrev_objectversions(opt_v));
1779 	else
1780 		return (showrev_sysinfo());
1781 }
1782 
1783 #ifdef __sparc
1784 static void
1785 findsym_output(uintptr_t *symlist, uintptr_t value, uintptr_t location)
1786 {
1787 	uintptr_t	*symbolp;
1788 
1789 	for (symbolp = symlist; *symbolp; symbolp++)
1790 		if (value == *symbolp)
1791 			mdb_printf("found %a at %a\n", value, location);
1792 }
1793 
1794 /*ARGSUSED*/
1795 static int
1796 findsym_cb(void *data, const GElf_Sym *sym, const char *name,
1797     const mdb_syminfo_t *sip, const char *obj)
1798 {
1799 	uint32_t	*text;
1800 	int		len;
1801 	int		i;
1802 	int		j;
1803 	uint8_t		rd;
1804 	uintptr_t	value;
1805 	int32_t		imm13;
1806 	uint8_t		op;
1807 	uint8_t		op3;
1808 	uintptr_t	*symlist = data;
1809 	size_t		size = sym->st_size;
1810 
1811 	/*
1812 	 * if the size of the symbol is 0, then this symbol must be for an
1813 	 * alternate entry point or just some global label. We will,
1814 	 * therefore, get back to the text that follows this symbol in
1815 	 * some other symbol
1816 	 */
1817 	if (size == 0)
1818 		return (0);
1819 
1820 	if (sym->st_shndx == SHN_UNDEF)
1821 		return (0);
1822 
1823 	text = alloca(size);
1824 
1825 	if (mdb_vread(text, size, sym->st_value) == -1) {
1826 		mdb_warn("failed to read text for %s", name);
1827 		return (0);
1828 	}
1829 
1830 	len = size / 4;
1831 	for (i = 0; i < len; i++) {
1832 		if (!IS_SETHI(text[i]))
1833 			continue;
1834 
1835 		rd = RD(text[i]);
1836 		value = IMM22(text[i]) << 10;
1837 
1838 		/*
1839 		 * see if we already have a match with just the sethi
1840 		 */
1841 		findsym_output(symlist, value, sym->st_value + i * 4);
1842 
1843 		/*
1844 		 * search from the sethi on until we hit a relevant instr
1845 		 */
1846 		for (j = i + 1; j < len; j++) {
1847 			if ((op = OP(text[j])) & OP_ARITH_MEM_MASK) {
1848 				op3 = OP3(text[j]);
1849 
1850 				if (RS1(text[j]) != rd)
1851 					goto instr_end;
1852 
1853 				/*
1854 				 * This is a simple tool; we only deal
1855 				 * with operations which take immediates
1856 				 */
1857 				if (I(text[j]) == 0)
1858 					goto instr_end;
1859 
1860 				/*
1861 				 * sign extend the immediate value
1862 				 */
1863 				imm13 = IMM13(text[j]);
1864 				imm13 <<= 19;
1865 				imm13 >>= 19;
1866 
1867 				if (op == OP_ARITH) {
1868 					/* arithmetic operations */
1869 					if (op3 & OP3_COMPLEX_MASK)
1870 						goto instr_end;
1871 
1872 					switch (op3 & ~OP3_CC_MASK) {
1873 					case OP3_OR:
1874 						value |= imm13;
1875 						break;
1876 					case OP3_ADD:
1877 						value += imm13;
1878 						break;
1879 					case OP3_XOR:
1880 						value ^= imm13;
1881 						break;
1882 					default:
1883 						goto instr_end;
1884 					}
1885 				} else {
1886 					/* loads and stores */
1887 					/* op3 == OP_MEM */
1888 
1889 					value += imm13;
1890 				}
1891 
1892 				findsym_output(symlist, value,
1893 				    sym->st_value + j * 4);
1894 instr_end:
1895 				/*
1896 				 * if we're clobbering rd, break
1897 				 */
1898 				if (RD(text[j]) == rd)
1899 					break;
1900 			} else if (IS_SETHI(text[j])) {
1901 				if (RD(text[j]) == rd)
1902 					break;
1903 			} else if (OP(text[j]) == 1) {
1904 				/*
1905 				 * see if a call clobbers an %o or %g
1906 				 */
1907 				if (rd <= R_O7)
1908 					break;
1909 			}
1910 		}
1911 	}
1912 
1913 	return (0);
1914 }
1915 
1916 static int
1917 cmd_findsym(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1918 {
1919 	uintptr_t *symlist;
1920 	uint_t optg = FALSE;
1921 	uint_t type;
1922 	int len, i;
1923 
1924 	i = mdb_getopts(argc, argv, 'g', MDB_OPT_SETBITS, TRUE, &optg, NULL);
1925 
1926 	argc -= i;
1927 	argv += i;
1928 
1929 	len = argc + ((flags & DCMD_ADDRSPEC) ? 1 : 0) + 1;
1930 
1931 	if (len <= 1)
1932 		return (DCMD_USAGE);
1933 
1934 	/*
1935 	 * Set up a NULL-terminated symbol list, and then iterate over the
1936 	 * symbol table, scanning each function for references to these symbols.
1937 	 */
1938 	symlist = mdb_alloc(len * sizeof (uintptr_t), UM_SLEEP | UM_GC);
1939 	len = 0;
1940 
1941 	for (i = 0; i < argc; i++, argv++) {
1942 		const char *str = argv->a_un.a_str;
1943 		uintptr_t value;
1944 		GElf_Sym sym;
1945 
1946 		if (argv->a_type == MDB_TYPE_STRING) {
1947 			if (strchr("+-", str[0]) != NULL)
1948 				return (DCMD_USAGE);
1949 			else if (str[0] >= '0' && str[0] <= '9')
1950 				value = mdb_strtoull(str);
1951 			else if (mdb_lookup_by_name(str, &sym) != 0) {
1952 				mdb_warn("symbol '%s' not found", str);
1953 				return (DCMD_USAGE);
1954 			} else
1955 				value = sym.st_value;
1956 		} else
1957 			value = argv[i].a_un.a_val;
1958 
1959 		if (value != NULL)
1960 			symlist[len++] = value;
1961 	}
1962 
1963 	if (flags & DCMD_ADDRSPEC)
1964 		symlist[len++] = addr;
1965 
1966 	symlist[len] = NULL;
1967 
1968 	if (optg)
1969 		type = MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_FUNC;
1970 	else
1971 		type = MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FUNC;
1972 
1973 	if (mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
1974 	    MDB_TGT_SYMTAB, type, findsym_cb, symlist) == -1) {
1975 		mdb_warn("failed to iterate over symbol table");
1976 		return (DCMD_ERR);
1977 	}
1978 
1979 	return (DCMD_OK);
1980 }
1981 #endif /* __sparc */
1982 
1983 static int
1984 dis_str2addr(const char *s, uintptr_t *addr)
1985 {
1986 	GElf_Sym sym;
1987 
1988 	if (s[0] >= '0' && s[0] <= '9') {
1989 		*addr = (uintptr_t)mdb_strtoull(s);
1990 		return (0);
1991 	}
1992 
1993 	if (mdb_tgt_lookup_by_name(mdb.m_target,
1994 	    MDB_TGT_OBJ_EVERY, s, &sym, NULL) == -1) {
1995 		mdb_warn("symbol '%s' not found\n", s);
1996 		return (-1);
1997 	}
1998 
1999 	*addr = (uintptr_t)sym.st_value;
2000 	return (0);
2001 }
2002 
2003 static int
2004 cmd_dis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2005 {
2006 	mdb_tgt_t *tgt = mdb.m_target;
2007 	mdb_disasm_t *dis = mdb.m_disasm;
2008 
2009 	uintptr_t oaddr, naddr;
2010 	mdb_tgt_as_t as;
2011 	mdb_tgt_status_t st;
2012 	char buf[BUFSIZ];
2013 	GElf_Sym sym;
2014 	int i;
2015 
2016 	uint_t opt_f = FALSE;		/* File-mode off by default */
2017 	uint_t opt_w = FALSE;		/* Window mode off by default */
2018 	uint_t opt_a = FALSE;		/* Raw-address mode off by default */
2019 	uint_t opt_b = FALSE;		/* Address & symbols off by default */
2020 	uintptr_t n = -1UL;		/* Length of window in instructions */
2021 	uintptr_t eaddr = 0;		/* Ending address; 0 if limited by n */
2022 
2023 	i = mdb_getopts(argc, argv,
2024 	    'f', MDB_OPT_SETBITS, TRUE, &opt_f,
2025 	    'w', MDB_OPT_SETBITS, TRUE, &opt_w,
2026 	    'a', MDB_OPT_SETBITS, TRUE, &opt_a,
2027 	    'b', MDB_OPT_SETBITS, TRUE, &opt_b,
2028 	    'n', MDB_OPT_UINTPTR, &n, NULL);
2029 
2030 	/*
2031 	 * Disgusting argument post-processing ... basically the idea is to get
2032 	 * the target address into addr, which we do by using the specified
2033 	 * expression value, looking up a string as a symbol name, or by
2034 	 * using the address specified as dot.
2035 	 */
2036 	if (i != argc) {
2037 		if (argc != 0 && (argc - i) == 1) {
2038 			if (argv[i].a_type == MDB_TYPE_STRING) {
2039 				if (argv[i].a_un.a_str[0] == '-')
2040 					return (DCMD_USAGE);
2041 
2042 				if (dis_str2addr(argv[i].a_un.a_str, &addr))
2043 					return (DCMD_ERR);
2044 			} else
2045 				addr = argv[i].a_un.a_val;
2046 		} else
2047 			return (DCMD_USAGE);
2048 	}
2049 
2050 	/*
2051 	 * If we're not in window mode yet, and some type of arguments were
2052 	 * specified, see if the address corresponds nicely to a function.
2053 	 * If not, turn on window mode; otherwise disassemble the function.
2054 	 */
2055 	if (opt_w == FALSE && (argc != i || (flags & DCMD_ADDRSPEC))) {
2056 		if (mdb_tgt_lookup_by_addr(tgt, addr,
2057 		    MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0 &&
2058 		    GELF_ST_TYPE(sym.st_info) == STT_FUNC) {
2059 			/*
2060 			 * If the symbol has a size then set our end address to
2061 			 * be the end of the function symbol we just located.
2062 			 */
2063 			if (sym.st_size != 0)
2064 				eaddr = addr + (uintptr_t)sym.st_size;
2065 		} else
2066 			opt_w = TRUE;
2067 	}
2068 
2069 	/*
2070 	 * Window-mode doesn't make sense in a loop.
2071 	 */
2072 	if (flags & DCMD_LOOP)
2073 		opt_w = FALSE;
2074 
2075 	/*
2076 	 * If -n was explicit, limit output to n instructions;
2077 	 * otherwise set n to some reasonable default
2078 	 */
2079 	if (n != -1UL)
2080 		eaddr = 0;
2081 	else
2082 		n = 10;
2083 
2084 	/*
2085 	 * If the state is IDLE (i.e. no address space), turn on -f.
2086 	 */
2087 	if (mdb_tgt_status(tgt, &st) == 0 && st.st_state == MDB_TGT_IDLE)
2088 		opt_f = TRUE;
2089 
2090 	if (opt_f)
2091 		as = MDB_TGT_AS_FILE;
2092 	else
2093 		as = MDB_TGT_AS_VIRT;
2094 
2095 	if (opt_w == FALSE) {
2096 		n++;
2097 		while ((eaddr == 0 && n-- != 0) || (addr < eaddr)) {
2098 			naddr = mdb_dis_ins2str(dis, tgt, as,
2099 			    buf, sizeof (buf), addr);
2100 			if (naddr == addr)
2101 				return (DCMD_ERR);
2102 			if (opt_a)
2103 				mdb_printf("%-#32p%8T%s\n", addr, buf);
2104 			else if (opt_b)
2105 				mdb_printf("%-#?p  %-#32a%8T%s\n",
2106 				    addr, addr, buf);
2107 			else
2108 				mdb_printf("%-#32a%8T%s\n", addr, buf);
2109 			addr = naddr;
2110 		}
2111 
2112 	} else {
2113 #ifdef __sparc
2114 		if (addr & 0x3) {
2115 			mdb_warn("address is not properly aligned\n");
2116 			return (DCMD_ERR);
2117 		}
2118 #endif
2119 
2120 		for (oaddr = mdb_dis_previns(dis, tgt, as, addr, n);
2121 		    oaddr < addr; oaddr = naddr) {
2122 			naddr = mdb_dis_ins2str(dis, tgt, as,
2123 			    buf, sizeof (buf), oaddr);
2124 			if (naddr == oaddr)
2125 				return (DCMD_ERR);
2126 			if (opt_a)
2127 				mdb_printf("%-#32p%8T%s\n", oaddr, buf);
2128 			else if (opt_b)
2129 				mdb_printf("%-#?p  %-#32a%8T%s\n",
2130 				    oaddr, oaddr, buf);
2131 			else
2132 				mdb_printf("%-#32a%8T%s\n", oaddr, buf);
2133 		}
2134 
2135 		if ((naddr = mdb_dis_ins2str(dis, tgt, as,
2136 		    buf, sizeof (buf), addr)) == addr)
2137 			return (DCMD_ERR);
2138 
2139 		mdb_printf("%<b>");
2140 		mdb_flush();
2141 		if (opt_a)
2142 			mdb_printf("%-#32p%8T%s%", addr, buf);
2143 		else if (opt_b)
2144 			mdb_printf("%-#?p  %-#32a%8T%s", addr, addr, buf);
2145 		else
2146 			mdb_printf("%-#32a%8T%s%", addr, buf);
2147 		mdb_printf("%</b>\n");
2148 
2149 		for (addr = naddr; n-- != 0; addr = naddr) {
2150 			naddr = mdb_dis_ins2str(dis, tgt, as,
2151 			    buf, sizeof (buf), addr);
2152 			if (naddr == addr)
2153 				return (DCMD_ERR);
2154 			if (opt_a)
2155 				mdb_printf("%-#32p%8T%s\n", addr, buf);
2156 			else if (opt_b)
2157 				mdb_printf("%-#?p  %-#32a%8T%s\n",
2158 				    addr, addr, buf);
2159 			else
2160 				mdb_printf("%-#32a%8T%s\n", addr, buf);
2161 		}
2162 	}
2163 
2164 	mdb_set_dot(addr);
2165 	return (DCMD_OK);
2166 }
2167 
2168 /*ARGSUSED*/
2169 static int
2170 walk_step(uintptr_t addr, const void *data, void *private)
2171 {
2172 	mdb_printf("%#lr\n", addr);
2173 	return (WALK_NEXT);
2174 }
2175 
2176 static int
2177 cmd_walk(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2178 {
2179 	int status;
2180 
2181 	if (argc < 1 || argc > 2 || argv[0].a_type != MDB_TYPE_STRING ||
2182 	    argv[argc - 1].a_type != MDB_TYPE_STRING)
2183 		return (DCMD_USAGE);
2184 
2185 	if (argc > 1) {
2186 		const char *name = argv[1].a_un.a_str;
2187 		mdb_var_t *v = mdb_nv_lookup(&mdb.m_nv, name);
2188 		const char *p;
2189 
2190 		if (v != NULL && (v->v_flags & MDB_NV_RDONLY) != 0) {
2191 			mdb_warn("variable %s is read-only\n", name);
2192 			return (DCMD_ABORT);
2193 		}
2194 
2195 		if (v == NULL && (p = strbadid(name)) != NULL) {
2196 			mdb_warn("'%c' may not be used in a variable "
2197 			    "name\n", *p);
2198 			return (DCMD_ABORT);
2199 		}
2200 
2201 		if (v == NULL && (v = mdb_nv_insert(&mdb.m_nv,
2202 		    name, NULL, 0, 0)) == NULL)
2203 			return (DCMD_ERR);
2204 
2205 		/*
2206 		 * If there already exists a vcb for this variable, we may be
2207 		 * calling ::walk in a loop.  We only create a vcb for this
2208 		 * variable on the first invocation.
2209 		 */
2210 		if (mdb_vcb_find(v, mdb.m_frame) == NULL)
2211 			mdb_vcb_insert(mdb_vcb_create(v), mdb.m_frame);
2212 	}
2213 
2214 	if (flags & DCMD_ADDRSPEC)
2215 		status = mdb_pwalk(argv->a_un.a_str, walk_step, NULL, addr);
2216 	else
2217 		status = mdb_walk(argv->a_un.a_str, walk_step, NULL);
2218 
2219 	if (status == -1) {
2220 		mdb_warn("failed to perform walk");
2221 		return (DCMD_ERR);
2222 	}
2223 
2224 	return (DCMD_OK);
2225 }
2226 
2227 static int
2228 cmd_walk_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
2229     const mdb_arg_t *argv)
2230 {
2231 	if (argc > 1)
2232 		return (1);
2233 
2234 	if (argc == 1) {
2235 		ASSERT(argv[0].a_type == MDB_TYPE_STRING);
2236 		return (mdb_tab_complete_walker(mcp, argv[0].a_un.a_str));
2237 	}
2238 
2239 	if (argc == 0 && flags & DCMD_TAB_SPACE)
2240 		return (mdb_tab_complete_walker(mcp, NULL));
2241 
2242 	return (1);
2243 }
2244 
2245 static ssize_t
2246 mdb_partial_xread(void *buf, size_t nbytes, uintptr_t addr, void *arg)
2247 {
2248 	ssize_t (*fp)(mdb_tgt_t *, const void *, size_t, uintptr_t) =
2249 	    (ssize_t (*)(mdb_tgt_t *, const void *, size_t, uintptr_t))arg;
2250 
2251 	return (fp(mdb.m_target, buf, nbytes, addr));
2252 }
2253 
2254 /* ARGSUSED3 */
2255 static ssize_t
2256 mdb_partial_pread(void *buf, size_t nbytes, physaddr_t addr, void *arg)
2257 {
2258 	return (mdb_tgt_pread(mdb.m_target, buf, nbytes, addr));
2259 }
2260 
2261 
2262 static int
2263 cmd_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2264 {
2265 	uint_t dflags =
2266 	    MDB_DUMP_ALIGN | MDB_DUMP_NEWDOT | MDB_DUMP_ASCII | MDB_DUMP_HEADER;
2267 	uint_t phys = FALSE;
2268 	uint_t file = FALSE;
2269 	uintptr_t group = 4;
2270 	uintptr_t length = 0;
2271 	uintptr_t width = 1;
2272 	mdb_tgt_status_t st;
2273 	int error;
2274 
2275 	if (mdb_getopts(argc, argv,
2276 	    'e', MDB_OPT_SETBITS, MDB_DUMP_ENDIAN, &dflags,
2277 	    'f', MDB_OPT_SETBITS, TRUE, &file,
2278 	    'g', MDB_OPT_UINTPTR, &group,
2279 	    'l', MDB_OPT_UINTPTR, &length,
2280 	    'p', MDB_OPT_SETBITS, TRUE, &phys,
2281 	    'q', MDB_OPT_CLRBITS, MDB_DUMP_ASCII, &dflags,
2282 	    'r', MDB_OPT_SETBITS, MDB_DUMP_RELATIVE, &dflags,
2283 	    's', MDB_OPT_SETBITS, MDB_DUMP_SQUISH, &dflags,
2284 	    't', MDB_OPT_SETBITS, MDB_DUMP_TRIM, &dflags,
2285 	    'u', MDB_OPT_CLRBITS, MDB_DUMP_ALIGN, &dflags,
2286 	    'v', MDB_OPT_SETBITS, MDB_DUMP_PEDANT, &dflags,
2287 	    'w', MDB_OPT_UINTPTR, &width, NULL) != argc)
2288 		return (DCMD_USAGE);
2289 
2290 	if ((phys && file) ||
2291 	    (width == 0) || (width > 0x10) ||
2292 	    (group == 0) || (group > 0x100) ||
2293 	    (mdb.m_dcount > 1 && length > 0))
2294 		return (DCMD_USAGE);
2295 	if (length == 0)
2296 		length = mdb.m_dcount;
2297 
2298 	/*
2299 	 * If neither -f nor -p were specified and the state is IDLE (i.e. no
2300 	 * address space), turn on -p.  This is so we can read large files.
2301 	 */
2302 	if (phys == FALSE && file == FALSE && mdb_tgt_status(mdb.m_target,
2303 	    &st) == 0 && st.st_state == MDB_TGT_IDLE)
2304 		phys = TRUE;
2305 
2306 	dflags |= MDB_DUMP_GROUP(group) | MDB_DUMP_WIDTH(width);
2307 	if (phys)
2308 		error = mdb_dump64(mdb_get_dot(), length, dflags,
2309 		    mdb_partial_pread, NULL);
2310 	else if (file)
2311 		error = mdb_dumpptr(addr, length, dflags,
2312 		    mdb_partial_xread, (void *)mdb_tgt_fread);
2313 	else
2314 		error = mdb_dumpptr(addr, length, dflags,
2315 		    mdb_partial_xread, (void *)mdb_tgt_vread);
2316 
2317 	return (((flags & DCMD_LOOP) || (error == -1)) ? DCMD_ABORT : DCMD_OK);
2318 }
2319 
2320 /*ARGSUSED*/
2321 static int
2322 cmd_echo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2323 {
2324 	if (flags & DCMD_ADDRSPEC)
2325 		return (DCMD_USAGE);
2326 
2327 	for (; argc-- != 0; argv++) {
2328 		if (argv->a_type == MDB_TYPE_STRING)
2329 			mdb_printf("%s ", argv->a_un.a_str);
2330 		else
2331 			mdb_printf("%llr ", argv->a_un.a_val);
2332 	}
2333 
2334 	mdb_printf("\n");
2335 	return (DCMD_OK);
2336 }
2337 
2338 /*ARGSUSED*/
2339 static int
2340 cmd_head(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2341 {
2342 	uint64_t cnt = 10;
2343 	const char *c;
2344 	mdb_pipe_t p;
2345 
2346 	if (!flags & DCMD_PIPE)
2347 		return (DCMD_USAGE);
2348 
2349 	if (argc == 1 || argc == 2) {
2350 		const char *num;
2351 
2352 		if (argc == 1) {
2353 			if (argv[0].a_type != MDB_TYPE_STRING ||
2354 			    *argv[0].a_un.a_str != '-')
2355 				return (DCMD_USAGE);
2356 
2357 			num = argv[0].a_un.a_str + 1;
2358 
2359 		} else {
2360 			if (argv[0].a_type != MDB_TYPE_STRING ||
2361 			    strcmp(argv[0].a_un.a_str, "-n") != 0)
2362 				return (DCMD_USAGE);
2363 
2364 			num = argv[1].a_un.a_str;
2365 		}
2366 
2367 		for (cnt = 0, c = num; *c != '\0' && isdigit(*c); c++)
2368 			cnt = cnt * 10 + (*c - '0');
2369 
2370 		if (*c != '\0')
2371 			return (DCMD_USAGE);
2372 
2373 	} else if (argc != 0) {
2374 		return (DCMD_USAGE);
2375 	}
2376 
2377 	mdb_get_pipe(&p);
2378 
2379 	if (p.pipe_data == NULL)
2380 		return (DCMD_OK);
2381 	p.pipe_len = MIN(p.pipe_len, cnt);
2382 
2383 	if (flags & DCMD_PIPE_OUT) {
2384 		mdb_set_pipe(&p);
2385 	} else {
2386 		while (p.pipe_len-- > 0)
2387 			mdb_printf("%lx\n", *p.pipe_data++);
2388 	}
2389 
2390 	return (DCMD_OK);
2391 }
2392 
2393 static void
2394 head_help(void)
2395 {
2396 	mdb_printf(
2397 	    "-n num\n or\n"
2398 	    "-num   pass only the first `num' elements in the pipe.\n"
2399 	    "\n%<b>Note:%</b> `num' is a decimal number.\n");
2400 }
2401 
2402 static int
2403 cmd_typeset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2404 {
2405 	int add_tag = 0, del_tag = 0;
2406 	const char *p;
2407 	mdb_var_t *v;
2408 
2409 	if (argc == 0)
2410 		return (cmd_vars(addr, flags, argc, argv));
2411 
2412 	if (argv->a_type == MDB_TYPE_STRING && (argv->a_un.a_str[0] == '-' ||
2413 	    argv->a_un.a_str[0] == '+')) {
2414 		if (argv->a_un.a_str[1] != 't')
2415 			return (DCMD_USAGE);
2416 		if (argv->a_un.a_str[0] == '-')
2417 			add_tag++;
2418 		else
2419 			del_tag++;
2420 		argc--;
2421 		argv++;
2422 	}
2423 
2424 	if (!(flags & DCMD_ADDRSPEC))
2425 		addr = 0; /* set variables to zero unless explicit addr given */
2426 
2427 	for (; argc-- != 0; argv++) {
2428 		if (argv->a_type != MDB_TYPE_STRING)
2429 			continue;
2430 
2431 		if (argv->a_un.a_str[0] == '-' || argv->a_un.a_str[0] == '+') {
2432 			mdb_warn("ignored bad option -- %s\n",
2433 			    argv->a_un.a_str);
2434 			continue;
2435 		}
2436 
2437 		if ((p = strbadid(argv->a_un.a_str)) != NULL) {
2438 			mdb_warn("'%c' may not be used in a variable "
2439 			    "name\n", *p);
2440 			return (DCMD_ERR);
2441 		}
2442 
2443 		if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL) {
2444 			v = mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str,
2445 			    NULL, addr, 0);
2446 		} else if (flags & DCMD_ADDRSPEC)
2447 			mdb_nv_set_value(v, addr);
2448 
2449 		if (v != NULL) {
2450 			if (add_tag)
2451 				v->v_flags |= MDB_NV_TAGGED;
2452 			if (del_tag)
2453 				v->v_flags &= ~MDB_NV_TAGGED;
2454 		}
2455 	}
2456 
2457 	return (DCMD_OK);
2458 }
2459 
2460 #ifndef _KMDB
2461 /*ARGSUSED*/
2462 static int
2463 cmd_context(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2464 {
2465 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
2466 		return (DCMD_USAGE);
2467 
2468 	if (mdb_tgt_setcontext(mdb.m_target, (void *)addr) == 0)
2469 		return (DCMD_OK);
2470 
2471 	return (DCMD_ERR);
2472 }
2473 #endif
2474 
2475 /*ARGSUSED*/
2476 static int
2477 cmd_prompt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2478 {
2479 	const char *p = "";
2480 
2481 	if (argc != 0) {
2482 		if (argc > 1 || argv->a_type != MDB_TYPE_STRING)
2483 			return (DCMD_USAGE);
2484 		p = argv->a_un.a_str;
2485 	}
2486 
2487 	(void) mdb_set_prompt(p);
2488 	return (DCMD_OK);
2489 }
2490 
2491 /*ARGSUSED*/
2492 static int
2493 cmd_term(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2494 {
2495 	mdb_printf("%s\n", mdb.m_termtype);
2496 
2497 	return (DCMD_OK);
2498 }
2499 
2500 /*ARGSUSED*/
2501 static int
2502 cmd_vtop(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2503 {
2504 	physaddr_t pa;
2505 	mdb_tgt_as_t as = MDB_TGT_AS_VIRT;
2506 
2507 	if (mdb_getopts(argc, argv, 'a', MDB_OPT_UINTPTR, (uintptr_t *)&as,
2508 	    NULL) != argc)
2509 		return (DCMD_USAGE);
2510 
2511 	if (mdb_tgt_vtop(mdb.m_target, as, addr, &pa) == -1) {
2512 		mdb_warn("failed to get physical mapping");
2513 		return (DCMD_ERR);
2514 	}
2515 
2516 	if (flags & DCMD_PIPE_OUT)
2517 		mdb_printf("%llr\n", pa);
2518 	else
2519 		mdb_printf("virtual %lr mapped to physical %llr\n", addr, pa);
2520 	return (DCMD_OK);
2521 }
2522 
2523 #define	EVENTS_OPT_A	0x1	/* ::events -a (show all events) */
2524 #define	EVENTS_OPT_V	0x2	/* ::events -v (verbose display) */
2525 
2526 static const char *
2527 event_action(const mdb_tgt_spec_desc_t *sp)
2528 {
2529 	if (!(sp->spec_flags & MDB_TGT_SPEC_HIDDEN) && sp->spec_data != NULL)
2530 		return (sp->spec_data);
2531 
2532 	return ("-");
2533 }
2534 
2535 static void
2536 print_evsep(void)
2537 {
2538 	static const char dash20[] = "--------------------";
2539 	mdb_printf("----- - -- -- -- %s%s --%s\n", dash20, dash20, dash20);
2540 }
2541 
2542 /*ARGSUSED*/
2543 static int
2544 print_event(mdb_tgt_t *t, void *private, int vid, void *data)
2545 {
2546 	uint_t opts = (uint_t)(uintptr_t)private;
2547 	mdb_tgt_spec_desc_t sp;
2548 	char s1[41], s2[22];
2549 	const char *s2str;
2550 	int visible;
2551 
2552 	(void) mdb_tgt_vespec_info(t, vid, &sp, s1, sizeof (s1));
2553 	visible = !(sp.spec_flags & (MDB_TGT_SPEC_HIDDEN|MDB_TGT_SPEC_DELETED));
2554 
2555 	if ((opts & EVENTS_OPT_A) || visible) {
2556 		int encoding = (!(sp.spec_flags & MDB_TGT_SPEC_DISABLED)) |
2557 		    (!(sp.spec_flags & MDB_TGT_SPEC_MATCHED) << 1);
2558 
2559 		char ldelim = "<<(["[encoding];
2560 		char rdelim = ">>)]"[encoding];
2561 
2562 		char state = "0-+*!"[sp.spec_state];
2563 
2564 		char tflag = "T "[!(sp.spec_flags & MDB_TGT_SPEC_STICKY)];
2565 		char aflag = "d "[!(sp.spec_flags & MDB_TGT_SPEC_AUTODIS)];
2566 
2567 		if (sp.spec_flags & MDB_TGT_SPEC_TEMPORARY)
2568 			tflag = 't'; /* TEMP takes precedence over STICKY */
2569 		if (sp.spec_flags & MDB_TGT_SPEC_AUTODEL)
2570 			aflag = 'D'; /* AUTODEL takes precedence over AUTODIS */
2571 		if (sp.spec_flags & MDB_TGT_SPEC_AUTOSTOP)
2572 			aflag = 's'; /* AUTOSTOP takes precedence over both */
2573 
2574 		if (opts & EVENTS_OPT_V) {
2575 			if (sp.spec_state == MDB_TGT_SPEC_IDLE ||
2576 			    sp.spec_state == MDB_TGT_SPEC_ERROR)
2577 				s2str = mdb_strerror(sp.spec_errno);
2578 			else
2579 				s2str = "-";
2580 		} else
2581 			s2str = event_action(&sp);
2582 
2583 		if (mdb_snprintf(s2, sizeof (s2), "%s", s2str) >= sizeof (s2))
2584 			(void) strabbr(s2, sizeof (s2));
2585 
2586 		if (vid > -10 && vid < 10)
2587 			mdb_printf("%c%2d %c", ldelim, vid, rdelim);
2588 		else
2589 			mdb_printf("%c%3d%c", ldelim, vid, rdelim);
2590 
2591 		mdb_printf(" %c %c%c %2u %2u %-40s %-21s\n",
2592 		    state, tflag, aflag, sp.spec_hits, sp.spec_limit, s1, s2);
2593 
2594 		if (opts & EVENTS_OPT_V) {
2595 			mdb_printf("%-17s%s\n", "", event_action(&sp));
2596 			print_evsep();
2597 		}
2598 	}
2599 
2600 	return (0);
2601 }
2602 
2603 /*ARGSUSED*/
2604 static int
2605 cmd_events(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2606 {
2607 	uint_t opts = 0;
2608 
2609 	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
2610 	    'a', MDB_OPT_SETBITS, EVENTS_OPT_A, &opts,
2611 	    'v', MDB_OPT_SETBITS, EVENTS_OPT_V, &opts, NULL) != argc)
2612 		return (DCMD_USAGE);
2613 
2614 
2615 	if (opts & EVENTS_OPT_V) {
2616 		mdb_printf("   ID S TA HT LM %-40s %-21s\n%-17s%s\n",
2617 		    "Description", "Status", "", "Action");
2618 	} else {
2619 		mdb_printf("   ID S TA HT LM %-40s %-21s\n",
2620 		    "Description", "Action");
2621 	}
2622 
2623 	print_evsep();
2624 	return (mdb_tgt_vespec_iter(mdb.m_target, print_event,
2625 	    (void *)(uintptr_t)opts));
2626 }
2627 
2628 static int
2629 tgt_status(const mdb_tgt_status_t *tsp)
2630 {
2631 	const char *format;
2632 	char buf[BUFSIZ];
2633 
2634 	if (tsp->st_flags & MDB_TGT_BUSY)
2635 		return (DCMD_OK);
2636 
2637 	if (tsp->st_pc != 0) {
2638 		if (mdb_dis_ins2str(mdb.m_disasm, mdb.m_target, MDB_TGT_AS_VIRT,
2639 		    buf, sizeof (buf), tsp->st_pc) != tsp->st_pc)
2640 			format = "target stopped at:\n%-#16a%8T%s\n";
2641 		else
2642 			format = "target stopped at %a:\n";
2643 		mdb_warn(format, tsp->st_pc, buf);
2644 	}
2645 
2646 	switch (tsp->st_state) {
2647 	case MDB_TGT_IDLE:
2648 		mdb_warn("target is idle\n");
2649 		break;
2650 	case MDB_TGT_RUNNING:
2651 		if (tsp->st_flags & MDB_TGT_DSTOP)
2652 			mdb_warn("target is running, stop directive pending\n");
2653 		else
2654 			mdb_warn("target is running\n");
2655 		break;
2656 	case MDB_TGT_STOPPED:
2657 		if (tsp->st_pc == 0)
2658 			mdb_warn("target is stopped\n");
2659 		break;
2660 	case MDB_TGT_UNDEAD:
2661 		mdb_warn("target has terminated\n");
2662 		break;
2663 	case MDB_TGT_DEAD:
2664 		mdb_warn("target is a core dump\n");
2665 		break;
2666 	case MDB_TGT_LOST:
2667 		mdb_warn("target is no longer under debugger control\n");
2668 		break;
2669 	}
2670 
2671 	mdb_set_dot(tsp->st_pc);
2672 	return (DCMD_OK);
2673 }
2674 
2675 /*
2676  * mdb continue/step commands take an optional signal argument, but the
2677  * corresponding kmdb versions don't.
2678  */
2679 #ifdef _KMDB
2680 #define	CONT_MAXARGS	0	/* no optional SIG argument */
2681 #else
2682 #define	CONT_MAXARGS	1
2683 #endif
2684 
2685 /*ARGSUSED*/
2686 static int
2687 cmd_cont_common(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
2688     int (*t_cont)(mdb_tgt_t *, mdb_tgt_status_t *), const char *name)
2689 {
2690 	mdb_tgt_t *t = mdb.m_target;
2691 	mdb_tgt_status_t st;
2692 	int sig = 0;
2693 
2694 	if ((flags & DCMD_ADDRSPEC) || argc > CONT_MAXARGS)
2695 		return (DCMD_USAGE);
2696 
2697 	if (argc > 0) {
2698 		if (argv->a_type == MDB_TYPE_STRING) {
2699 			if (proc_str2sig(argv->a_un.a_str, &sig) == -1) {
2700 				mdb_warn("invalid signal name -- %s\n",
2701 				    argv->a_un.a_str);
2702 				return (DCMD_USAGE);
2703 			}
2704 		} else
2705 			sig = (int)(intmax_t)argv->a_un.a_val;
2706 	}
2707 
2708 	(void) mdb_tgt_status(t, &st);
2709 
2710 	if (st.st_state == MDB_TGT_IDLE && mdb_tgt_run(t, 0, NULL) == -1) {
2711 		if (errno != EMDB_TGT)
2712 			mdb_warn("failed to create new target");
2713 		return (DCMD_ERR);
2714 	}
2715 
2716 	if (sig != 0 && mdb_tgt_signal(t, sig) == -1) {
2717 		mdb_warn("failed to post signal %d", sig);
2718 		return (DCMD_ERR);
2719 	}
2720 
2721 	if (st.st_state == MDB_TGT_IDLE && t_cont == &mdb_tgt_step) {
2722 		(void) mdb_tgt_status(t, &st);
2723 		return (tgt_status(&st));
2724 	}
2725 
2726 	if (t_cont(t, &st) == -1) {
2727 		if (errno != EMDB_TGT)
2728 			mdb_warn("failed to %s target", name);
2729 		return (DCMD_ERR);
2730 	}
2731 
2732 	return (tgt_status(&st));
2733 }
2734 
2735 static int
2736 cmd_step(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2737 {
2738 	int (*func)(mdb_tgt_t *, mdb_tgt_status_t *) = &mdb_tgt_step;
2739 	const char *name = "single-step";
2740 
2741 	if (argc > 0 && argv->a_type == MDB_TYPE_STRING) {
2742 		if (strcmp(argv->a_un.a_str, "out") == 0) {
2743 			func = &mdb_tgt_step_out;
2744 			name = "step (out)";
2745 			argv++;
2746 			argc--;
2747 		} else if (strcmp(argv->a_un.a_str, "over") == 0) {
2748 			func = &mdb_tgt_next;
2749 			name = "step (over)";
2750 			argv++;
2751 			argc--;
2752 		}
2753 	}
2754 
2755 	return (cmd_cont_common(addr, flags, argc, argv, func, name));
2756 }
2757 
2758 static int
2759 cmd_step_out(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2760 {
2761 	return (cmd_cont_common(addr, flags, argc, argv,
2762 	    &mdb_tgt_step_out, "step (out)"));
2763 }
2764 
2765 static int
2766 cmd_next(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2767 {
2768 	return (cmd_cont_common(addr, flags, argc, argv,
2769 	    &mdb_tgt_next, "step (over)"));
2770 }
2771 
2772 static int
2773 cmd_cont(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2774 {
2775 	return (cmd_cont_common(addr, flags, argc, argv,
2776 	    &mdb_tgt_continue, "continue"));
2777 }
2778 
2779 #ifndef _KMDB
2780 /*ARGSUSED*/
2781 static int
2782 cmd_run(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2783 {
2784 	if (flags & DCMD_ADDRSPEC)
2785 		return (DCMD_USAGE);
2786 
2787 	if (mdb_tgt_run(mdb.m_target, argc, argv) == -1) {
2788 		if (errno != EMDB_TGT)
2789 			mdb_warn("failed to create new target");
2790 		return (DCMD_ERR);
2791 	}
2792 	return (cmd_cont(NULL, 0, 0, NULL));
2793 }
2794 #endif
2795 
2796 /*
2797  * To simplify the implementation of :d, :z, and ::delete, we use the sp
2798  * parameter to store the criteria for what to delete.  If spec_base is set,
2799  * we delete vespecs with a matching address.  If spec_id is set, we delete
2800  * vespecs with a matching id.  Otherwise, we delete all vespecs.  We bump
2801  * sp->spec_size so the caller can tell how many vespecs were deleted.
2802  */
2803 static int
2804 ve_delete(mdb_tgt_t *t, mdb_tgt_spec_desc_t *sp, int vid, void *data)
2805 {
2806 	mdb_tgt_spec_desc_t spec;
2807 	int status = -1;
2808 
2809 	if (vid < 0)
2810 		return (0); /* skip over target implementation events */
2811 
2812 	if (sp->spec_base != NULL) {
2813 		(void) mdb_tgt_vespec_info(t, vid, &spec, NULL, 0);
2814 		if (sp->spec_base - spec.spec_base < spec.spec_size)
2815 			status = mdb_tgt_vespec_delete(t, vid);
2816 	} else if (sp->spec_id == 0) {
2817 		(void) mdb_tgt_vespec_info(t, vid, &spec, NULL, 0);
2818 		if (!(spec.spec_flags & MDB_TGT_SPEC_STICKY))
2819 			status = mdb_tgt_vespec_delete(t, vid);
2820 	} else if (sp->spec_id == vid)
2821 		status = mdb_tgt_vespec_delete(t, vid);
2822 
2823 	if (status == 0) {
2824 		if (data != NULL)
2825 			strfree(data);
2826 		sp->spec_size++;
2827 	}
2828 
2829 	return (0);
2830 }
2831 
2832 static int
2833 ve_delete_spec(mdb_tgt_spec_desc_t *sp)
2834 {
2835 	(void) mdb_tgt_vespec_iter(mdb.m_target,
2836 	    (mdb_tgt_vespec_f *)ve_delete, sp);
2837 
2838 	if (sp->spec_size == 0) {
2839 		if (sp->spec_id != 0 || sp->spec_base != NULL)
2840 			mdb_warn("no traced events matched description\n");
2841 	}
2842 
2843 	return (DCMD_OK);
2844 }
2845 
2846 /*ARGSUSED*/
2847 static int
2848 cmd_zapall(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2849 {
2850 	mdb_tgt_spec_desc_t spec;
2851 
2852 	if ((flags & DCMD_ADDRSPEC) || argc != 0)
2853 		return (DCMD_USAGE);
2854 
2855 	bzero(&spec, sizeof (spec));
2856 	return (ve_delete_spec(&spec));
2857 }
2858 
2859 static int
2860 cmd_delete(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2861 {
2862 	mdb_tgt_spec_desc_t spec;
2863 
2864 	if (((flags & DCMD_ADDRSPEC) && argc > 0) || argc > 1)
2865 		return (DCMD_USAGE);
2866 
2867 	bzero(&spec, sizeof (spec));
2868 
2869 	if (flags & DCMD_ADDRSPEC)
2870 		spec.spec_base = addr;
2871 	else if (argc == 0)
2872 		spec.spec_base = mdb_get_dot();
2873 	else if (argv->a_type == MDB_TYPE_STRING &&
2874 	    strcmp(argv->a_un.a_str, "all") != 0)
2875 		spec.spec_id = (int)(intmax_t)mdb_strtonum(argv->a_un.a_str,
2876 		    10);
2877 	else if (argv->a_type == MDB_TYPE_IMMEDIATE)
2878 		spec.spec_id = (int)(intmax_t)argv->a_un.a_val;
2879 
2880 	return (ve_delete_spec(&spec));
2881 }
2882 
2883 static int
2884 cmd_write(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2885 {
2886 	mdb_tgt_as_t as;
2887 	int rdback = mdb.m_flags & MDB_FL_READBACK;
2888 	mdb_tgt_addr_t naddr;
2889 	size_t forced_size = 0;
2890 	boolean_t opt_p, opt_o, opt_l;
2891 	uint64_t val = 0;
2892 	int i;
2893 
2894 	opt_p = opt_o = opt_l = B_FALSE;
2895 
2896 	i = mdb_getopts(argc, argv,
2897 	    'p', MDB_OPT_SETBITS, B_TRUE, &opt_p,
2898 	    'o', MDB_OPT_SETBITS, B_TRUE, &opt_o,
2899 	    'l', MDB_OPT_UINTPTR_SET, &opt_l, (uintptr_t *)&forced_size, NULL);
2900 
2901 	if (!(flags & DCMD_ADDRSPEC))
2902 		return (DCMD_USAGE);
2903 
2904 	if (opt_p && opt_o) {
2905 		mdb_warn("-o and -p are incompatible\n");
2906 		return (DCMD_USAGE);
2907 	}
2908 
2909 	argc -= i;
2910 	argv += i;
2911 
2912 	if (argc == 0)
2913 		return (DCMD_USAGE);
2914 
2915 	switch (argv[0].a_type) {
2916 	case MDB_TYPE_STRING:
2917 		val = mdb_strtoull(argv[0].a_un.a_str);
2918 		break;
2919 	case MDB_TYPE_IMMEDIATE:
2920 		val = argv[0].a_un.a_val;
2921 		break;
2922 	default:
2923 		return (DCMD_USAGE);
2924 	}
2925 
2926 	if (opt_p)
2927 		as = MDB_TGT_AS_PHYS;
2928 	else if (opt_o)
2929 		as = MDB_TGT_AS_FILE;
2930 	else
2931 		as = MDB_TGT_AS_VIRT;
2932 
2933 	if (opt_l)
2934 		naddr = write_var_uint(as, addr, val, forced_size, rdback);
2935 	else
2936 		naddr = write_ctf_uint(as, addr, val, rdback);
2937 
2938 	if (addr == naddr) {
2939 		mdb_warn("failed to write %llr at address %#llx", val, addr);
2940 		return (DCMD_ERR);
2941 	}
2942 
2943 	return (DCMD_OK);
2944 }
2945 
2946 void
2947 write_help(void)
2948 {
2949 	mdb_printf(
2950 	    "-l length  force a write with the specified length in bytes\n"
2951 	    "-o         write data to the object file location specified\n"
2952 	    "-p         write data to the physical address specified\n"
2953 	    "\n"
2954 	    "Attempts to write the given value to the address provided.\n"
2955 	    "If -l is not specified, the address must be the position of a\n"
2956 	    "symbol that is either of integer, pointer, or enum type. The\n"
2957 	    "type and the size of the symbol are inferred by the CTF found\n"
2958 	    "in the provided address. The length of the write is guaranteed\n"
2959 	    "to be the inferred size of the symbol.\n"
2960 	    "\n"
2961 	    "If no CTF data exists, or the address provided is not a symbol\n"
2962 	    "of integer or pointer type, then the write fails. At that point\n"
2963 	    "the user can force the write by using the '-l' option and\n"
2964 	    "specifying its length.\n"
2965 	    "\n"
2966 	    "Note that forced writes with a length that are bigger than\n"
2967 	    "the size of the biggest data pointer supported are not allowed."
2968 	    "\n");
2969 }
2970 
2971 static void
2972 srcexec_file_help(void)
2973 {
2974 	mdb_printf(
2975 "The library of macros delivered with previous versions of Solaris have been\n"
2976 "superseded by the dcmds and walkers provided by MDB.  See ::help for\n"
2977 "commands that can be used to list the available dcmds and walkers.\n"
2978 "\n"
2979 "Aliases have been created for several of the more popular macros.  To see\n"
2980 "the list of aliased macros, as well as their native MDB equivalents,\n"
2981 "type $M.\n");
2982 
2983 #ifdef _KMDB
2984 	mdb_printf(
2985 "When invoked, the $< and $<< dcmds will consult the macro alias list.  If an\n"
2986 "alias cannot be found, an attempt will be made to locate a data type whose\n"
2987 "name corresponds to the requested macro.  If such a type can be found, it\n"
2988 "will be displayed using the ::print dcmd.\n");
2989 #else
2990 	mdb_printf(
2991 "When invoked, the $< and $<< dcmds will first attempt to locate a macro with\n"
2992 "the indicated name.  If no macro can be found, and if no alias exists for\n"
2993 "this macro, an attempt will be made to locate a data type whose name\n"
2994 "corresponds to the requested macro.  If such a type can be found, it will be\n"
2995 "displayed using the ::print dcmd.\n");
2996 #endif
2997 }
2998 
2999 static void
3000 events_help(void)
3001 {
3002 	mdb_printf("Options:\n"
3003 	    "-a       show all events, including internal debugger events\n"
3004 	    "-v       show verbose display, including inactivity reason\n"
3005 	    "\nOutput Columns:\n"
3006 	    "ID       decimal event specifier id number:\n"
3007 	    "    [ ]  event tracing is enabled\n"
3008 	    "    ( )  event tracing is disabled\n"
3009 	    "    < >  target is currently stopped on this type of event\n\n"
3010 	    "S        event specifier state:\n"
3011 	    "     -   event specifier is idle (not applicable yet)\n"
3012 	    "     +   event specifier is active\n"
3013 	    "     *   event specifier is armed (target program running)\n"
3014 	    "     !   error occurred while attempting to arm event\n\n"
3015 	    "TA       event specifier flags:\n"
3016 	    "     t   event specifier is temporary (delete at next stop)\n"
3017 	    "     T   event specifier is sticky (::delete all has no effect)\n"
3018 	    "     d   event specifier will be disabled when HT = LM\n"
3019 	    "     D   event specifier will be deleted when HT = LM\n"
3020 	    "     s   target will automatically stop when HT = LM\n\n"
3021 	    "HT       hit count (number of times event has occurred)\n"
3022 	    "LM       hit limit (limit for autostop, disable, delete)\n");
3023 }
3024 
3025 static void
3026 dump_help(void)
3027 {
3028 	mdb_printf(
3029 	    "-e    adjust for endianness\n"
3030 	    "      (assumes 4-byte words; use -g to change word size)\n"
3031 #ifdef _KMDB
3032 	    "-f    no effect\n"
3033 #else
3034 	    "-f    dump from object file\n"
3035 #endif
3036 	    "-g n  display bytes in groups of n\n"
3037 	    "      (default is 4; n must be a power of 2, divide line width)\n"
3038 	    "-l n  display n bytes\n"
3039 	    "      (default is 1; rounded up to multiple of line width)\n"
3040 	    "-p    dump from physical memory\n"
3041 	    "-q    don't print ASCII\n"
3042 	    "-r    use relative numbering (automatically sets -u)\n"
3043 	    "-s    elide repeated lines\n"
3044 	    "-t    only read from and display contents of specified addresses\n"
3045 	    "      (default is to read and print entire lines)\n"
3046 	    "-u    un-align output\n"
3047 	    "      (default is to align output at paragraph boundary)\n"
3048 	    "-w n  display n 16-byte paragraphs per line\n"
3049 	    "      (default is 1, maximum is 16)\n");
3050 }
3051 
3052 /*
3053  * Table of built-in dcmds associated with the root 'mdb' module.  Future
3054  * expansion of this program should be done here, or through the external
3055  * loadable module interface.
3056  */
3057 const mdb_dcmd_t mdb_dcmd_builtins[] = {
3058 
3059 	/*
3060 	 * dcmds common to both mdb and kmdb
3061 	 */
3062 	{ ">", "variable-name", "assign variable", cmd_assign_variable },
3063 	{ "/", "fmt-list", "format data from virtual as", cmd_print_core },
3064 	{ "\\", "fmt-list", "format data from physical as", cmd_print_phys },
3065 	{ "@", "fmt-list", "format data from physical as", cmd_print_phys },
3066 	{ "=", "fmt-list", "format immediate value", cmd_print_value },
3067 	{ "$<", "macro-name", "replace input with macro",
3068 	    cmd_exec_file, srcexec_file_help },
3069 	{ "$<<", "macro-name", "source macro",
3070 	    cmd_src_file, srcexec_file_help},
3071 	{ "$%", NULL, NULL, cmd_quit },
3072 	{ "$?", NULL, "print status and registers", cmd_notsup },
3073 	{ "$a", NULL, NULL, cmd_algol },
3074 	{ "$b", "[-av]", "list traced software events",
3075 	    cmd_events, events_help },
3076 	{ "$c", "?[cnt]", "print stack backtrace", cmd_notsup },
3077 	{ "$C", "?[cnt]", "print stack backtrace", cmd_notsup },
3078 	{ "$d", NULL, "get/set default output radix", cmd_radix },
3079 	{ "$D", "?[mode,...]", NULL, cmd_dbmode },
3080 	{ "$e", NULL, "print listing of global symbols", cmd_globals },
3081 	{ "$f", NULL, "print listing of source files", cmd_files },
3082 	{ "$m", "?[name]", "print address space mappings", cmd_mappings },
3083 	{ "$M", NULL, "list macro aliases", cmd_macalias_list },
3084 	{ "$P", "[prompt]", "set debugger prompt string", cmd_prompt },
3085 	{ "$q", NULL, "quit debugger", cmd_quit },
3086 	{ "$Q", NULL, "quit debugger", cmd_quit },
3087 	{ "$r", NULL, "print general-purpose registers", cmd_notsup },
3088 	{ "$s", NULL, "get/set symbol matching distance", cmd_symdist },
3089 	{ "$v", NULL, "print non-zero variables", cmd_nzvars },
3090 	{ "$V", "[mode]", "get/set disassembly mode", cmd_dismode },
3091 	{ "$w", NULL, "get/set output page width", cmd_pgwidth },
3092 	{ "$W", NULL, "re-open target in write mode", cmd_reopen },
3093 	{ ":a", ":[cmd...]", "set read access watchpoint", cmd_oldwpr },
3094 	{ ":b", ":[cmd...]", "breakpoint at the specified address", cmd_oldbp },
3095 	{ ":d", "?[id|all]", "delete traced software events", cmd_delete },
3096 	{ ":p", ":[cmd...]", "set execute access watchpoint", cmd_oldwpx },
3097 	{ ":S", NULL, NULL, cmd_step },
3098 	{ ":w", ":[cmd...]", "set write access watchpoint", cmd_oldwpw },
3099 	{ ":z", NULL, "delete all traced software events", cmd_zapall },
3100 	{ "array", ":[type count] [variable]", "print each array element's "
3101 	    "address", cmd_array },
3102 	{ "bp", "?[+/-dDestT] [-c cmd] [-n count] sym ...", "breakpoint at the "
3103 	    "specified addresses or symbols", cmd_bp, bp_help },
3104 	{ "dcmds", "[[-n] pattern]",
3105 	    "list available debugger commands", cmd_dcmds, cmd_dcmds_help },
3106 	{ "delete", "?[id|all]", "delete traced software events", cmd_delete },
3107 	{ "dis", "?[-abfw] [-n cnt] [addr]", "disassemble near addr", cmd_dis },
3108 	{ "disasms", NULL, "list available disassemblers", cmd_disasms },
3109 	{ "dismode", "[mode]", "get/set disassembly mode", cmd_dismode },
3110 	{ "dmods", "[-l] [mod]", "list loaded debugger modules", cmd_dmods },
3111 	{ "dump", "?[-eqrstu] [-f|-p] [-g bytes] [-l bytes] [-w paragraphs]",
3112 	    "dump memory from specified address", cmd_dump, dump_help },
3113 	{ "echo", "args ...", "echo arguments", cmd_echo },
3114 	{ "enum", "?[-ex] enum [name]", "print an enumeration", cmd_enum,
3115 	    enum_help },
3116 	{ "eval", "command", "evaluate the specified command", cmd_eval },
3117 	{ "events", "[-av]", "list traced software events",
3118 	    cmd_events, events_help },
3119 	{ "evset", "?[+/-dDestT] [-c cmd] [-n count] id ...",
3120 	    "set software event specifier attributes", cmd_evset, evset_help },
3121 	{ "files", "[object]", "print listing of source files", cmd_files },
3122 #ifdef __sparc
3123 	{ "findsym", "?[-g] [symbol|addr ...]", "search for symbol references "
3124 	    "in all known functions", cmd_findsym, NULL },
3125 #endif
3126 	{ "formats", NULL, "list format specifiers", cmd_formats },
3127 	{ "grep", "?expr", "print dot if expression is true", cmd_grep },
3128 	{ "head", "-num|-n num", "limit number of elements in pipe", cmd_head,
3129 	    head_help },
3130 	{ "help", "[cmd]", "list commands/command help", cmd_help, NULL,
3131 	    cmd_help_tab },
3132 	{ "list", "?type member [variable]",
3133 	    "walk list using member as link pointer", cmd_list, NULL,
3134 	    mdb_tab_complete_mt },
3135 	{ "map", "?expr", "print dot after evaluating expression", cmd_map },
3136 	{ "mappings", "?[name]", "print address space mappings", cmd_mappings },
3137 	{ "nm", "?[-DPdghnopuvx] [-f format] [-t types] [object]",
3138 	    "print symbols", cmd_nm, nm_help },
3139 	{ "nmadd", ":[-fo] [-e end] [-s size] name",
3140 	    "add name to private symbol table", cmd_nmadd, nmadd_help },
3141 	{ "nmdel", "name", "remove name from private symbol table", cmd_nmdel },
3142 	{ "obey", NULL, NULL, cmd_obey },
3143 	{ "objects", "[-v]", "print load objects information", cmd_objects },
3144 	{ "offsetof", "type member", "print the offset of a given struct "
3145 	    "or union member", cmd_offsetof, NULL, mdb_tab_complete_mt },
3146 	{ "print", "?[-aCdhiLptx] [-c lim] [-l lim] [type] [member|offset ...]",
3147 	    "print the contents of a data structure", cmd_print, print_help,
3148 	    cmd_print_tab },
3149 	{ "printf", "?format type member ...", "print and format the "
3150 	    "member(s) of a data structure", cmd_printf, printf_help,
3151 	    cmd_printf_tab },
3152 	{ "regs", NULL, "print general purpose registers", cmd_notsup },
3153 	{ "set", "[-wF] [+/-o opt] [-s dist] [-I path] [-L path] [-P prompt]",
3154 	    "get/set debugger properties", cmd_set },
3155 	{ "showrev", "[-pv]", "print version information", cmd_showrev },
3156 	{ "sizeof", "type", "print the size of a type", cmd_sizeof, NULL,
3157 	    cmd_sizeof_tab },
3158 	{ "stack", "?[cnt]", "print stack backtrace", cmd_notsup },
3159 	{ "stackregs", "?", "print stack backtrace and registers",
3160 	    cmd_notsup },
3161 	{ "status", NULL, "print summary of current target", cmd_notsup },
3162 	{ "term", NULL, "display current terminal type", cmd_term },
3163 	{ "typeset", "[+/-t] var ...", "set variable attributes", cmd_typeset },
3164 	{ "typedef", "[-c model | -d | -l | -r file | -w file ] [type] [name]",
3165 		"create synthetic types", cmd_typedef, cmd_typedef_help },
3166 	{ "unset", "[name ...]", "unset variables", cmd_unset },
3167 	{ "vars", "[-npt]", "print listing of variables", cmd_vars },
3168 	{ "version", NULL, "print debugger version string", cmd_version },
3169 	{ "vtop", ":[-a as]", "print physical mapping of virtual address",
3170 	    cmd_vtop },
3171 	{ "walk", "?name [variable]", "walk data structure", cmd_walk, NULL,
3172 	    cmd_walk_tab },
3173 	{ "walkers", "[[-n] pattern]", "list available walkers",
3174 	    cmd_walkers, cmd_walkers_help },
3175 	{ "whatis", ":[-aikqv]", "given an address, return information",
3176 	    cmd_whatis, whatis_help },
3177 	{ "whence", "[-v] name ...", "show source of walk or dcmd", cmd_which },
3178 	{ "which", "[-v] name ...", "show source of walk or dcmd", cmd_which },
3179 	{ "write", "?[-op] [-l len] value",
3180 	    "write value to the provided memory location", cmd_write,
3181 	    write_help },
3182 	{ "xdata", NULL, "print list of external data buffers", cmd_xdata },
3183 
3184 #ifdef _KMDB
3185 	/*
3186 	 * dcmds specific to kmdb, or which have kmdb-specific arguments
3187 	 */
3188 	{ "?", "fmt-list", "format data from virtual as", cmd_print_core },
3189 	{ ":c", NULL, "continue target execution", cmd_cont },
3190 	{ ":e", NULL, "step target over next instruction", cmd_next },
3191 	{ ":s", NULL, "single-step target to next instruction", cmd_step },
3192 	{ ":u", NULL, "step target out of current function", cmd_step_out },
3193 	{ "cont", NULL, "continue target execution", cmd_cont },
3194 	{ "load", "[-sd] module", "load debugger module", cmd_load, load_help },
3195 	{ "next", NULL, "step target over next instruction", cmd_next },
3196 	{ "quit", "[-u]", "quit debugger", cmd_quit, quit_help },
3197 	{ "step", "[ over | out ]",
3198 	    "single-step target to next instruction", cmd_step },
3199 	{ "unload", "[-d] module", "unload debugger module", cmd_unload,
3200 	    unload_help },
3201 	{ "wp", ":[+/-dDelstT] [-rwx] [-pi] [-c cmd] [-n count] [-L size]",
3202 	    "set a watchpoint at the specified address", cmd_wp, wp_help },
3203 
3204 #else
3205 	/*
3206 	 * dcmds specific to mdb, or which have mdb-specific arguments
3207 	 */
3208 	{ "?", "fmt-list", "format data from object file", cmd_print_object },
3209 	{ "$>", "[file]", "log session to a file", cmd_old_log },
3210 	{ "$g", "?", "get/set C++ demangling options", cmd_demflags },
3211 	{ "$G", NULL, "enable/disable C++ demangling support", cmd_demangle },
3212 	{ "$i", NULL, "print signals that are ignored", cmd_notsup },
3213 	{ "$l", NULL, "print the representative thread's lwp id", cmd_notsup },
3214 	{ "$p", ":", "change debugger target context", cmd_context },
3215 	{ "$x", NULL, "print floating point registers", cmd_notsup },
3216 	{ "$X", NULL, "print floating point registers", cmd_notsup },
3217 	{ "$y", NULL, "print floating point registers", cmd_notsup },
3218 	{ "$Y", NULL, "print floating point registers", cmd_notsup },
3219 	{ ":A", "?[core|pid]", "attach to process or core file", cmd_notsup },
3220 	{ ":c", "[SIG]", "continue target execution", cmd_cont },
3221 	{ ":e", "[SIG]", "step target over next instruction", cmd_next },
3222 	{ ":i", ":", "ignore signal (delete all matching events)", cmd_notsup },
3223 	{ ":k", NULL, "forcibly kill and release target", cmd_notsup },
3224 	{ ":t", "?[+/-dDestT] [-c cmd] [-n count] SIG ...", "stop on delivery "
3225 	    "of the specified signals", cmd_sigbp, sigbp_help },
3226 	{ ":r", "[ args ... ]", "run a new target process", cmd_run },
3227 	{ ":R", NULL, "release the previously attached process", cmd_notsup },
3228 	{ ":s", "[SIG]", "single-step target to next instruction", cmd_step },
3229 	{ ":u", "[SIG]", "step target out of current function", cmd_step_out },
3230 	{ "attach", "?[core|pid]",
3231 	    "attach to process or core file", cmd_notsup },
3232 	{ "cat", "[file ...]", "concatenate and display files", cmd_cat },
3233 	{ "cont", "[SIG]", "continue target execution", cmd_cont },
3234 	{ "context", ":", "change debugger target context", cmd_context },
3235 	{ "dem", "name ...", "demangle C++ symbol names", cmd_demstr },
3236 	{ "fltbp", "?[+/-dDestT] [-c cmd] [-n count] fault ...",
3237 	    "stop on machine fault", cmd_fltbp, fltbp_help },
3238 	{ "fpregs", NULL, "print floating point registers", cmd_notsup },
3239 	{ "kill", NULL, "forcibly kill and release target", cmd_notsup },
3240 	{ "load", "[-s] module", "load debugger module", cmd_load, load_help },
3241 	{ "log", "[-d | [-e] file]", "log session to a file", cmd_log },
3242 	{ "next", "[SIG]", "step target over next instruction", cmd_next },
3243 	{ "quit", NULL, "quit debugger", cmd_quit },
3244 	{ "release", NULL,
3245 	    "release the previously attached process", cmd_notsup },
3246 	{ "run", "[ args ... ]", "run a new target process", cmd_run },
3247 	{ "sigbp", "?[+/-dDestT] [-c cmd] [-n count] SIG ...", "stop on "
3248 	    "delivery of the specified signals", cmd_sigbp, sigbp_help },
3249 	{ "step", "[ over | out ] [SIG]",
3250 	    "single-step target to next instruction", cmd_step },
3251 	{ "sysbp", "?[+/-dDestT] [-io] [-c cmd] [-n count] syscall ...",
3252 	    "stop on entry or exit from system call", cmd_sysbp, sysbp_help },
3253 	{ "unload", "module", "unload debugger module", cmd_unload },
3254 	{ "wp", ":[+/-dDelstT] [-rwx] [-c cmd] [-n count] [-L size]",
3255 	    "set a watchpoint at the specified address", cmd_wp, wp_help },
3256 #endif
3257 
3258 	{ NULL }
3259 };
3260