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