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