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