xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb.c (revision 8a6a72fd51dfee674c6daa8a222f68b1532516ab)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*8a6a72fdSaf  * Common Development and Distribution License (the "License").
6*8a6a72fdSaf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*8a6a72fdSaf  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * Modular Debugger (MDB)
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  * Refer to the white paper "A Modular Debugger for Solaris" for information
327c478bd9Sstevel@tonic-gate  * on the design, features, and goals of MDB.  See /shared/sac/PSARC/1999/169
337c478bd9Sstevel@tonic-gate  * for copies of the paper and related documentation.
347c478bd9Sstevel@tonic-gate  *
357c478bd9Sstevel@tonic-gate  * This file provides the basic construction and destruction of the debugger's
367c478bd9Sstevel@tonic-gate  * global state, as well as the main execution loop, mdb_run().  MDB maintains
377c478bd9Sstevel@tonic-gate  * a stack of execution frames (mdb_frame_t's) that keep track of its current
387c478bd9Sstevel@tonic-gate  * state, including a stack of input and output buffers, walk and memory
397c478bd9Sstevel@tonic-gate  * garbage collect lists, and a list of commands (mdb_cmd_t's).  As the
407c478bd9Sstevel@tonic-gate  * parser consumes input, it fills in a list of commands to execute, and then
417c478bd9Sstevel@tonic-gate  * invokes mdb_call(), below.  A command consists of a dcmd, telling us
427c478bd9Sstevel@tonic-gate  * what function to execute, and a list of arguments and other invocation-
437c478bd9Sstevel@tonic-gate  * specific data.  Each frame may have more than one command, kept on a list,
447c478bd9Sstevel@tonic-gate  * when multiple commands are separated by | operators.  New frames may be
457c478bd9Sstevel@tonic-gate  * stacked on old ones by nested calls to mdb_run: this occurs when, for
467c478bd9Sstevel@tonic-gate  * example, in the middle of processing one input source (such as a file
477c478bd9Sstevel@tonic-gate  * or the terminal), we invoke a dcmd that in turn calls mdb_eval().  mdb_eval
487c478bd9Sstevel@tonic-gate  * will construct a new frame whose input source is the string passed to
497c478bd9Sstevel@tonic-gate  * the eval function, and then execute this frame to completion.
507c478bd9Sstevel@tonic-gate  */
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #include <sys/param.h>
537c478bd9Sstevel@tonic-gate #include <stropts.h>
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate #define	_MDB_PRIVATE
567c478bd9Sstevel@tonic-gate #include <mdb/mdb.h>
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate #include <mdb/mdb_context.h>
597c478bd9Sstevel@tonic-gate #include <mdb/mdb_argvec.h>
607c478bd9Sstevel@tonic-gate #include <mdb/mdb_signal.h>
617c478bd9Sstevel@tonic-gate #include <mdb/mdb_macalias.h>
627c478bd9Sstevel@tonic-gate #include <mdb/mdb_module.h>
637c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
647c478bd9Sstevel@tonic-gate #include <mdb/mdb_string.h>
657c478bd9Sstevel@tonic-gate #include <mdb/mdb_callb.h>
667c478bd9Sstevel@tonic-gate #include <mdb/mdb_debug.h>
677c478bd9Sstevel@tonic-gate #include <mdb/mdb_frame.h>
687c478bd9Sstevel@tonic-gate #include <mdb/mdb_conf.h>
697c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h>
707c478bd9Sstevel@tonic-gate #include <mdb/mdb_lex.h>
717c478bd9Sstevel@tonic-gate #include <mdb/mdb_io.h>
727c478bd9Sstevel@tonic-gate #ifdef _KMDB
737c478bd9Sstevel@tonic-gate #include <kmdb/kmdb_module.h>
747c478bd9Sstevel@tonic-gate #endif
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate /*
777c478bd9Sstevel@tonic-gate  * Macro for testing if a dcmd's return status (x) indicates that we should
787c478bd9Sstevel@tonic-gate  * abort the current loop or pipeline.
797c478bd9Sstevel@tonic-gate  */
807c478bd9Sstevel@tonic-gate #define	DCMD_ABORTED(x)	((x) == DCMD_USAGE || (x) == DCMD_ABORT)
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate extern const mdb_dcmd_t mdb_dcmd_builtins[];
837c478bd9Sstevel@tonic-gate extern mdb_dis_ctor_f *const mdb_dis_builtins[];
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate /*
867c478bd9Sstevel@tonic-gate  * Variable discipline for toggling MDB_FL_PSYM based on the value of the
877c478bd9Sstevel@tonic-gate  * undocumented '_' variable.  Once adb(1) has been removed from the system,
887c478bd9Sstevel@tonic-gate  * we should just remove this functionality and always disable PSYM for macros.
897c478bd9Sstevel@tonic-gate  */
907c478bd9Sstevel@tonic-gate static uintmax_t
917c478bd9Sstevel@tonic-gate psym_disc_get(const mdb_var_t *v)
927c478bd9Sstevel@tonic-gate {
937c478bd9Sstevel@tonic-gate 	int i = (mdb.m_flags & MDB_FL_PSYM) ? 1 : 0;
947c478bd9Sstevel@tonic-gate 	int j = (MDB_NV_VALUE(v) != 0) ? 1 : 0;
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate 	if ((i ^ j) == 0)
977c478bd9Sstevel@tonic-gate 		MDB_NV_VALUE((mdb_var_t *)v) = j ^ 1;
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate 	return (MDB_NV_VALUE(v));
1007c478bd9Sstevel@tonic-gate }
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate static void
1037c478bd9Sstevel@tonic-gate psym_disc_set(mdb_var_t *v, uintmax_t value)
1047c478bd9Sstevel@tonic-gate {
1057c478bd9Sstevel@tonic-gate 	if (value == 0)
1067c478bd9Sstevel@tonic-gate 		mdb.m_flags |= MDB_FL_PSYM;
1077c478bd9Sstevel@tonic-gate 	else
1087c478bd9Sstevel@tonic-gate 		mdb.m_flags &= ~MDB_FL_PSYM;
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	MDB_NV_VALUE(v) = value;
1117c478bd9Sstevel@tonic-gate }
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate /*
1147c478bd9Sstevel@tonic-gate  * Variable discipline for making <1 (most recent offset) behave properly.
1157c478bd9Sstevel@tonic-gate  */
1167c478bd9Sstevel@tonic-gate static uintmax_t
1177c478bd9Sstevel@tonic-gate roff_disc_get(const mdb_var_t *v)
1187c478bd9Sstevel@tonic-gate {
1197c478bd9Sstevel@tonic-gate 	return (MDB_NV_VALUE(v));
1207c478bd9Sstevel@tonic-gate }
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate static void
1237c478bd9Sstevel@tonic-gate roff_disc_set(mdb_var_t *v, uintmax_t value)
1247c478bd9Sstevel@tonic-gate {
1257c478bd9Sstevel@tonic-gate 	mdb_nv_set_value(mdb.m_proffset, MDB_NV_VALUE(v));
1267c478bd9Sstevel@tonic-gate 	MDB_NV_VALUE(v) = value;
1277c478bd9Sstevel@tonic-gate }
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate /*
1307c478bd9Sstevel@tonic-gate  * Variable discipline for exporting the representative thread.
1317c478bd9Sstevel@tonic-gate  */
1327c478bd9Sstevel@tonic-gate static uintmax_t
1337c478bd9Sstevel@tonic-gate thr_disc_get(const mdb_var_t *v)
1347c478bd9Sstevel@tonic-gate {
1357c478bd9Sstevel@tonic-gate 	mdb_tgt_status_t s;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	if (mdb.m_target != NULL && mdb_tgt_status(mdb.m_target, &s) == 0)
1387c478bd9Sstevel@tonic-gate 		return (s.st_tid);
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 	return (MDB_NV_VALUE(v));
1417c478bd9Sstevel@tonic-gate }
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate const char **
1447c478bd9Sstevel@tonic-gate mdb_path_alloc(const char *s, size_t *newlen)
1457c478bd9Sstevel@tonic-gate {
1467c478bd9Sstevel@tonic-gate 	char *format = mdb_alloc(strlen(s) * 2 + 1, UM_NOSLEEP);
1477c478bd9Sstevel@tonic-gate 	const char **path;
1487c478bd9Sstevel@tonic-gate 	char *p, *q;
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	struct utsname uts;
1517c478bd9Sstevel@tonic-gate 	size_t len;
1527c478bd9Sstevel@tonic-gate 	int i;
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 	mdb_arg_t arg_i, arg_m, arg_p, arg_r, arg_t, arg_R, arg_V;
1557c478bd9Sstevel@tonic-gate 	mdb_argvec_t argv;
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	static const char *empty_path[] = { NULL };
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	if (format == NULL)
1607c478bd9Sstevel@tonic-gate 		goto nomem;
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 	while (*s == ':')
1637c478bd9Sstevel@tonic-gate 		s++; /* strip leading delimiters */
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	if (*s == '\0') {
1667c478bd9Sstevel@tonic-gate 		*newlen = 0;
1677c478bd9Sstevel@tonic-gate 		return (empty_path);
1687c478bd9Sstevel@tonic-gate 	}
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	(void) strcpy(format, s);
1717c478bd9Sstevel@tonic-gate 	mdb_argvec_create(&argv);
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	/*
1747c478bd9Sstevel@tonic-gate 	 * %i embedded in path string expands to ISA.
1757c478bd9Sstevel@tonic-gate 	 */
1767c478bd9Sstevel@tonic-gate 	arg_i.a_type = MDB_TYPE_STRING;
1777c478bd9Sstevel@tonic-gate 	if (mdb.m_target != NULL)
1787c478bd9Sstevel@tonic-gate 		arg_i.a_un.a_str = mdb_tgt_isa(mdb.m_target);
1797c478bd9Sstevel@tonic-gate 	else
1807c478bd9Sstevel@tonic-gate 		arg_i.a_un.a_str = mdb_conf_isa();
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 	/*
1837c478bd9Sstevel@tonic-gate 	 * %p embedded in path string expands to the platform name.
1847c478bd9Sstevel@tonic-gate 	 */
1857c478bd9Sstevel@tonic-gate 	arg_p.a_type = MDB_TYPE_STRING;
1867c478bd9Sstevel@tonic-gate 	if (mdb.m_target != NULL)
1877c478bd9Sstevel@tonic-gate 		arg_p.a_un.a_str = mdb_tgt_platform(mdb.m_target);
1887c478bd9Sstevel@tonic-gate 	else
1897c478bd9Sstevel@tonic-gate 		arg_p.a_un.a_str = mdb_conf_platform();
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	/*
1927c478bd9Sstevel@tonic-gate 	 * %r embedded in path string expands to root directory, or
1937c478bd9Sstevel@tonic-gate 	 * to the empty string if root is "/" (to avoid // in paths).
1947c478bd9Sstevel@tonic-gate 	 */
1957c478bd9Sstevel@tonic-gate 	arg_r.a_type = MDB_TYPE_STRING;
1967c478bd9Sstevel@tonic-gate 	arg_r.a_un.a_str = strcmp(mdb.m_root, "/") ? mdb.m_root : "";
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	/*
1997c478bd9Sstevel@tonic-gate 	 * %t embedded in path string expands to the target name.
2007c478bd9Sstevel@tonic-gate 	 */
2017c478bd9Sstevel@tonic-gate 	arg_t.a_type = MDB_TYPE_STRING;
2027c478bd9Sstevel@tonic-gate 	arg_t.a_un.a_str = mdb.m_target ? mdb_tgt_name(mdb.m_target) : "none";
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	/*
2057c478bd9Sstevel@tonic-gate 	 * %R and %V expand to uname -r (release) and uname -v (version).
2067c478bd9Sstevel@tonic-gate 	 */
2077c478bd9Sstevel@tonic-gate 	if (mdb.m_target == NULL || mdb_tgt_uname(mdb.m_target, &uts) < 0)
2087c478bd9Sstevel@tonic-gate 		mdb_conf_uname(&uts);
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	arg_m.a_type = MDB_TYPE_STRING;
2117c478bd9Sstevel@tonic-gate 	arg_m.a_un.a_str = uts.machine;
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	arg_R.a_type = MDB_TYPE_STRING;
2147c478bd9Sstevel@tonic-gate 	arg_R.a_un.a_str = uts.release;
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	arg_V.a_type = MDB_TYPE_STRING;
2177c478bd9Sstevel@tonic-gate 	if (mdb.m_flags & MDB_FL_LATEST)
2187c478bd9Sstevel@tonic-gate 		arg_V.a_un.a_str = "latest";
2197c478bd9Sstevel@tonic-gate 	else
2207c478bd9Sstevel@tonic-gate 		arg_V.a_un.a_str = uts.version;
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	/*
2237c478bd9Sstevel@tonic-gate 	 * In order to expand the buffer, we examine the format string for
2247c478bd9Sstevel@tonic-gate 	 * our % tokens and construct an argvec, replacing each % token
2257c478bd9Sstevel@tonic-gate 	 * with %s along the way.  If we encounter an unknown token, we
2267c478bd9Sstevel@tonic-gate 	 * shift over the remaining format buffer and stick in %%.
2277c478bd9Sstevel@tonic-gate 	 */
2287c478bd9Sstevel@tonic-gate 	for (q = format; (q = strchr(q, '%')) != NULL; q++) {
2297c478bd9Sstevel@tonic-gate 		switch (q[1]) {
2307c478bd9Sstevel@tonic-gate 		case 'i':
2317c478bd9Sstevel@tonic-gate 			mdb_argvec_append(&argv, &arg_i);
2327c478bd9Sstevel@tonic-gate 			*++q = 's';
2337c478bd9Sstevel@tonic-gate 			break;
2347c478bd9Sstevel@tonic-gate 		case 'm':
2357c478bd9Sstevel@tonic-gate 			mdb_argvec_append(&argv, &arg_m);
2367c478bd9Sstevel@tonic-gate 			*++q = 's';
2377c478bd9Sstevel@tonic-gate 			break;
2387c478bd9Sstevel@tonic-gate 		case 'p':
2397c478bd9Sstevel@tonic-gate 			mdb_argvec_append(&argv, &arg_p);
2407c478bd9Sstevel@tonic-gate 			*++q = 's';
2417c478bd9Sstevel@tonic-gate 			break;
2427c478bd9Sstevel@tonic-gate 		case 'r':
2437c478bd9Sstevel@tonic-gate 			mdb_argvec_append(&argv, &arg_r);
2447c478bd9Sstevel@tonic-gate 			*++q = 's';
2457c478bd9Sstevel@tonic-gate 			break;
2467c478bd9Sstevel@tonic-gate 		case 't':
2477c478bd9Sstevel@tonic-gate 			mdb_argvec_append(&argv, &arg_t);
2487c478bd9Sstevel@tonic-gate 			*++q = 's';
2497c478bd9Sstevel@tonic-gate 			break;
2507c478bd9Sstevel@tonic-gate 		case 'R':
2517c478bd9Sstevel@tonic-gate 			mdb_argvec_append(&argv, &arg_R);
2527c478bd9Sstevel@tonic-gate 			*++q = 's';
2537c478bd9Sstevel@tonic-gate 			break;
2547c478bd9Sstevel@tonic-gate 		case 'V':
2557c478bd9Sstevel@tonic-gate 			mdb_argvec_append(&argv, &arg_V);
2567c478bd9Sstevel@tonic-gate 			*++q = 's';
2577c478bd9Sstevel@tonic-gate 			break;
2587c478bd9Sstevel@tonic-gate 		default:
2597c478bd9Sstevel@tonic-gate 			bcopy(q + 1, q + 2, strlen(q));
2607c478bd9Sstevel@tonic-gate 			*++q = '%';
2617c478bd9Sstevel@tonic-gate 		}
2627c478bd9Sstevel@tonic-gate 	}
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	/*
2657c478bd9Sstevel@tonic-gate 	 * We're now ready to use our printf engine to format the final string.
2667c478bd9Sstevel@tonic-gate 	 * Take one lap with a NULL buffer to determine how long the final
2677c478bd9Sstevel@tonic-gate 	 * string will be, allocate it, and format it.
2687c478bd9Sstevel@tonic-gate 	 */
2697c478bd9Sstevel@tonic-gate 	len = mdb_iob_asnprintf(NULL, 0, format, argv.a_data);
2707c478bd9Sstevel@tonic-gate 	if ((p = mdb_alloc(len + 1, UM_NOSLEEP)) != NULL)
2717c478bd9Sstevel@tonic-gate 		(void) mdb_iob_asnprintf(p, len + 1, format, argv.a_data);
2727c478bd9Sstevel@tonic-gate 	else
2737c478bd9Sstevel@tonic-gate 		goto nomem;
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 	mdb_argvec_zero(&argv);
2767c478bd9Sstevel@tonic-gate 	mdb_argvec_destroy(&argv);
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	mdb_free(format, strlen(s) * 2 + 1);
2797c478bd9Sstevel@tonic-gate 	format = NULL;
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	/*
2827c478bd9Sstevel@tonic-gate 	 * Compress the string to exclude any leading delimiters.
2837c478bd9Sstevel@tonic-gate 	 */
2847c478bd9Sstevel@tonic-gate 	for (q = p; *q == ':'; q++)
2857c478bd9Sstevel@tonic-gate 		continue;
2867c478bd9Sstevel@tonic-gate 	if (q != p)
2877c478bd9Sstevel@tonic-gate 		bcopy(q, p, strlen(q) + 1);
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	/*
2907c478bd9Sstevel@tonic-gate 	 * Count up the number of delimited elements.  A sequence of
2917c478bd9Sstevel@tonic-gate 	 * consecutive delimiters is only counted once.
2927c478bd9Sstevel@tonic-gate 	 */
2937c478bd9Sstevel@tonic-gate 	for (i = 1, q = p; (q = strchr(q, ':')) != NULL; i++) {
2947c478bd9Sstevel@tonic-gate 		while (*q == ':')
2957c478bd9Sstevel@tonic-gate 			q++;
2967c478bd9Sstevel@tonic-gate 	}
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	if ((path = mdb_alloc(sizeof (char *) * (i + 1), UM_NOSLEEP)) == NULL) {
2997c478bd9Sstevel@tonic-gate 		mdb_free(p, len + 1);
3007c478bd9Sstevel@tonic-gate 		goto nomem;
3017c478bd9Sstevel@tonic-gate 	}
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	for (i = 0, q = strtok(p, ":"); q != NULL; q = strtok(NULL, ":"))
3047c478bd9Sstevel@tonic-gate 		path[i++] = q;
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	path[i] = NULL;
3077c478bd9Sstevel@tonic-gate 	*newlen = len + 1;
3087c478bd9Sstevel@tonic-gate 	return (path);
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate nomem:
3117c478bd9Sstevel@tonic-gate 	warn("failed to allocate memory for path");
3127c478bd9Sstevel@tonic-gate 	if (format != NULL)
3137c478bd9Sstevel@tonic-gate 		mdb_free(format, strlen(s) * 2 + 1);
3147c478bd9Sstevel@tonic-gate 	*newlen = 0;
3157c478bd9Sstevel@tonic-gate 	return (empty_path);
3167c478bd9Sstevel@tonic-gate }
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate const char **
3197c478bd9Sstevel@tonic-gate mdb_path_dup(const char *path[], size_t pathlen, size_t *npathlenp)
3207c478bd9Sstevel@tonic-gate {
3217c478bd9Sstevel@tonic-gate 	char **npath;
3227c478bd9Sstevel@tonic-gate 	int i, j;
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 	for (i = 0; path[i] != NULL; i++)
3257c478bd9Sstevel@tonic-gate 		continue; /* count the path elements */
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	npath = mdb_zalloc(sizeof (char *) * (i + 1), UM_SLEEP);
3287c478bd9Sstevel@tonic-gate 	if (pathlen > 0) {
3297c478bd9Sstevel@tonic-gate 		npath[0] = mdb_alloc(pathlen, UM_SLEEP);
3307c478bd9Sstevel@tonic-gate 		bcopy(path[0], npath[0], pathlen);
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 	for (j = 1; j < i; j++)
3347c478bd9Sstevel@tonic-gate 		npath[j] = npath[0] + (path[j] - path[0]);
3357c478bd9Sstevel@tonic-gate 	npath[i] = NULL;
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	*npathlenp = pathlen;
3387c478bd9Sstevel@tonic-gate 	return ((const char **)npath);
3397c478bd9Sstevel@tonic-gate }
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate void
3427c478bd9Sstevel@tonic-gate mdb_path_free(const char *path[], size_t pathlen)
3437c478bd9Sstevel@tonic-gate {
3447c478bd9Sstevel@tonic-gate 	int i;
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	for (i = 0; path[i] != NULL; i++)
3477c478bd9Sstevel@tonic-gate 		continue; /* count the path elements */
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 	if (i > 0) {
3507c478bd9Sstevel@tonic-gate 		mdb_free((void *)path[0], pathlen);
3517c478bd9Sstevel@tonic-gate 		mdb_free(path, sizeof (char *) * (i + 1));
3527c478bd9Sstevel@tonic-gate 	}
3537c478bd9Sstevel@tonic-gate }
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate /*
3567c478bd9Sstevel@tonic-gate  * Convert path string "s" to canonical form, expanding any %o tokens that are
3577c478bd9Sstevel@tonic-gate  * found within the path.  The old path string is specified by "path", a buffer
3587c478bd9Sstevel@tonic-gate  * of size MAXPATHLEN which is then overwritten with the new path string.
3597c478bd9Sstevel@tonic-gate  */
3607c478bd9Sstevel@tonic-gate static const char *
3617c478bd9Sstevel@tonic-gate path_canon(char *path, const char *s)
3627c478bd9Sstevel@tonic-gate {
3637c478bd9Sstevel@tonic-gate 	char *p = path;
3647c478bd9Sstevel@tonic-gate 	char *q = p + MAXPATHLEN - 1;
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	char old[MAXPATHLEN];
3677c478bd9Sstevel@tonic-gate 	char c;
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 	(void) strcpy(old, p);
3707c478bd9Sstevel@tonic-gate 	*q = '\0';
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 	while (p < q && (c = *s++) != '\0') {
3737c478bd9Sstevel@tonic-gate 		if (c == '%') {
3747c478bd9Sstevel@tonic-gate 			if ((c = *s++) == 'o') {
3757c478bd9Sstevel@tonic-gate 				(void) strncpy(p, old, (size_t)(q - p));
3767c478bd9Sstevel@tonic-gate 				p += strlen(p);
3777c478bd9Sstevel@tonic-gate 			} else {
3787c478bd9Sstevel@tonic-gate 				*p++ = '%';
3797c478bd9Sstevel@tonic-gate 				if (p < q && c != '\0')
3807c478bd9Sstevel@tonic-gate 					*p++ = c;
3817c478bd9Sstevel@tonic-gate 				else
3827c478bd9Sstevel@tonic-gate 					break;
3837c478bd9Sstevel@tonic-gate 			}
3847c478bd9Sstevel@tonic-gate 		} else
3857c478bd9Sstevel@tonic-gate 			*p++ = c;
3867c478bd9Sstevel@tonic-gate 	}
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	*p = '\0';
3897c478bd9Sstevel@tonic-gate 	return (path);
3907c478bd9Sstevel@tonic-gate }
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate void
3937c478bd9Sstevel@tonic-gate mdb_set_ipath(const char *path)
3947c478bd9Sstevel@tonic-gate {
3957c478bd9Sstevel@tonic-gate 	if (mdb.m_ipath != NULL)
3967c478bd9Sstevel@tonic-gate 		mdb_path_free(mdb.m_ipath, mdb.m_ipathlen);
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate 	path = path_canon(mdb.m_ipathstr, path);
3997c478bd9Sstevel@tonic-gate 	mdb.m_ipath = mdb_path_alloc(path, &mdb.m_ipathlen);
4007c478bd9Sstevel@tonic-gate }
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate void
4037c478bd9Sstevel@tonic-gate mdb_set_lpath(const char *path)
4047c478bd9Sstevel@tonic-gate {
4057c478bd9Sstevel@tonic-gate 	if (mdb.m_lpath != NULL)
4067c478bd9Sstevel@tonic-gate 		mdb_path_free(mdb.m_lpath, mdb.m_lpathlen);
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	path = path_canon(mdb.m_lpathstr, path);
4097c478bd9Sstevel@tonic-gate 	mdb.m_lpath = mdb_path_alloc(path, &mdb.m_lpathlen);
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate #ifdef _KMDB
4127c478bd9Sstevel@tonic-gate 	kmdb_module_path_set(mdb.m_lpath, mdb.m_lpathlen);
4137c478bd9Sstevel@tonic-gate #endif
4147c478bd9Sstevel@tonic-gate }
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate static void
4177c478bd9Sstevel@tonic-gate prompt_update(void)
4187c478bd9Sstevel@tonic-gate {
4197c478bd9Sstevel@tonic-gate 	(void) mdb_snprintf(mdb.m_prompt, sizeof (mdb.m_prompt),
4207c478bd9Sstevel@tonic-gate 	    mdb.m_promptraw);
4217c478bd9Sstevel@tonic-gate 	mdb.m_promptlen = strlen(mdb.m_prompt);
4227c478bd9Sstevel@tonic-gate }
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate const char *
4257c478bd9Sstevel@tonic-gate mdb_get_prompt(void)
4267c478bd9Sstevel@tonic-gate {
4277c478bd9Sstevel@tonic-gate 	if (mdb.m_promptlen == 0)
4287c478bd9Sstevel@tonic-gate 		return (NULL);
4297c478bd9Sstevel@tonic-gate 	else
4307c478bd9Sstevel@tonic-gate 		return (mdb.m_prompt);
4317c478bd9Sstevel@tonic-gate }
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate int
4347c478bd9Sstevel@tonic-gate mdb_set_prompt(const char *p)
4357c478bd9Sstevel@tonic-gate {
4367c478bd9Sstevel@tonic-gate 	size_t len = strlen(p);
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	if (len > MDB_PROMPTLEN) {
4397c478bd9Sstevel@tonic-gate 		warn("prompt may not exceed %d characters\n", MDB_PROMPTLEN);
4407c478bd9Sstevel@tonic-gate 		return (0);
4417c478bd9Sstevel@tonic-gate 	}
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 	(void) strcpy(mdb.m_promptraw, p);
4447c478bd9Sstevel@tonic-gate 	prompt_update();
4457c478bd9Sstevel@tonic-gate 	return (1);
4467c478bd9Sstevel@tonic-gate }
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate static mdb_frame_t frame0;
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate void
4517c478bd9Sstevel@tonic-gate mdb_create(const char *execname, const char *arg0)
4527c478bd9Sstevel@tonic-gate {
4537c478bd9Sstevel@tonic-gate 	static const mdb_nv_disc_t psym_disc = { psym_disc_set, psym_disc_get };
4547c478bd9Sstevel@tonic-gate 	static const mdb_nv_disc_t roff_disc = { roff_disc_set, roff_disc_get };
4557c478bd9Sstevel@tonic-gate 	static const mdb_nv_disc_t thr_disc = { NULL, thr_disc_get };
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	static char rootdir[MAXPATHLEN];
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	const mdb_dcmd_t *dcp;
4607c478bd9Sstevel@tonic-gate 	int i;
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 	bzero(&mdb, sizeof (mdb_t));
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 	mdb.m_flags = MDB_FL_PSYM | MDB_FL_PAGER | MDB_FL_BPTNOSYMSTOP |
4657c478bd9Sstevel@tonic-gate 	    MDB_FL_READBACK;
4667c478bd9Sstevel@tonic-gate 	mdb.m_radix = MDB_DEF_RADIX;
4677c478bd9Sstevel@tonic-gate 	mdb.m_nargs = MDB_DEF_NARGS;
4687c478bd9Sstevel@tonic-gate 	mdb.m_histlen = MDB_DEF_HISTLEN;
4697c478bd9Sstevel@tonic-gate 	mdb.m_armemlim = MDB_DEF_ARRMEM;
4707c478bd9Sstevel@tonic-gate 	mdb.m_arstrlim = MDB_DEF_ARRSTR;
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	mdb.m_pname = strbasename(arg0);
4737c478bd9Sstevel@tonic-gate 	if (strcmp(mdb.m_pname, "adb") == 0) {
4747c478bd9Sstevel@tonic-gate 		mdb.m_flags |= MDB_FL_NOMODS | MDB_FL_ADB | MDB_FL_REPLAST;
4757c478bd9Sstevel@tonic-gate 		mdb.m_flags &= ~MDB_FL_PAGER;
4767c478bd9Sstevel@tonic-gate 	}
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate 	mdb.m_ipathstr = mdb_zalloc(MAXPATHLEN, UM_SLEEP);
4797c478bd9Sstevel@tonic-gate 	mdb.m_lpathstr = mdb_zalloc(MAXPATHLEN, UM_SLEEP);
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 	(void) strncpy(rootdir, execname, sizeof (rootdir));
4827c478bd9Sstevel@tonic-gate 	rootdir[sizeof (rootdir) - 1] = '\0';
4837c478bd9Sstevel@tonic-gate 	(void) strdirname(rootdir);
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate 	if (strcmp(strbasename(rootdir), "sparcv9") == 0 ||
4867c478bd9Sstevel@tonic-gate 	    strcmp(strbasename(rootdir), "sparcv7") == 0 ||
4877c478bd9Sstevel@tonic-gate 	    strcmp(strbasename(rootdir), "amd64") == 0 ||
4887c478bd9Sstevel@tonic-gate 	    strcmp(strbasename(rootdir), "i86") == 0)
4897c478bd9Sstevel@tonic-gate 		(void) strdirname(rootdir);
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	if (strcmp(strbasename(rootdir), "bin") == 0) {
4927c478bd9Sstevel@tonic-gate 		(void) strdirname(rootdir);
4937c478bd9Sstevel@tonic-gate 		if (strcmp(strbasename(rootdir), "usr") == 0)
4947c478bd9Sstevel@tonic-gate 			(void) strdirname(rootdir);
4957c478bd9Sstevel@tonic-gate 	} else
4967c478bd9Sstevel@tonic-gate 		(void) strcpy(rootdir, "/");
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	mdb.m_root = rootdir;
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate 	mdb.m_rminfo.mi_dvers = MDB_API_VERSION;
5017c478bd9Sstevel@tonic-gate 	mdb.m_rminfo.mi_dcmds = mdb_dcmd_builtins;
5027c478bd9Sstevel@tonic-gate 	mdb.m_rminfo.mi_walkers = NULL;
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	(void) mdb_nv_create(&mdb.m_rmod.mod_walkers, UM_SLEEP);
5057c478bd9Sstevel@tonic-gate 	(void) mdb_nv_create(&mdb.m_rmod.mod_dcmds, UM_SLEEP);
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	mdb.m_rmod.mod_name = mdb.m_pname;
5087c478bd9Sstevel@tonic-gate 	mdb.m_rmod.mod_info = &mdb.m_rminfo;
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 	(void) mdb_nv_create(&mdb.m_disasms, UM_SLEEP);
5117c478bd9Sstevel@tonic-gate 	(void) mdb_nv_create(&mdb.m_modules, UM_SLEEP);
5127c478bd9Sstevel@tonic-gate 	(void) mdb_nv_create(&mdb.m_dcmds, UM_SLEEP);
5137c478bd9Sstevel@tonic-gate 	(void) mdb_nv_create(&mdb.m_walkers, UM_SLEEP);
5147c478bd9Sstevel@tonic-gate 	(void) mdb_nv_create(&mdb.m_nv, UM_SLEEP);
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	mdb.m_dot = mdb_nv_insert(&mdb.m_nv, ".", NULL, 0, MDB_NV_PERSIST);
5177c478bd9Sstevel@tonic-gate 	mdb.m_rvalue = mdb_nv_insert(&mdb.m_nv, "0", NULL, 0, MDB_NV_PERSIST);
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	mdb.m_roffset =
5207c478bd9Sstevel@tonic-gate 	    mdb_nv_insert(&mdb.m_nv, "1", &roff_disc, 0, MDB_NV_PERSIST);
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 	mdb.m_proffset = mdb_nv_insert(&mdb.m_nv, "2", NULL, 0, MDB_NV_PERSIST);
5237c478bd9Sstevel@tonic-gate 	mdb.m_rcount = mdb_nv_insert(&mdb.m_nv, "9", NULL, 0, MDB_NV_PERSIST);
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 	(void) mdb_nv_insert(&mdb.m_nv, "b", NULL, 0, MDB_NV_PERSIST);
5267c478bd9Sstevel@tonic-gate 	(void) mdb_nv_insert(&mdb.m_nv, "d", NULL, 0, MDB_NV_PERSIST);
5277c478bd9Sstevel@tonic-gate 	(void) mdb_nv_insert(&mdb.m_nv, "e", NULL, 0, MDB_NV_PERSIST);
5287c478bd9Sstevel@tonic-gate 	(void) mdb_nv_insert(&mdb.m_nv, "m", NULL, 0, MDB_NV_PERSIST);
5297c478bd9Sstevel@tonic-gate 	(void) mdb_nv_insert(&mdb.m_nv, "t", NULL, 0, MDB_NV_PERSIST);
5307c478bd9Sstevel@tonic-gate 	(void) mdb_nv_insert(&mdb.m_nv, "_", &psym_disc, 0, MDB_NV_PERSIST);
5317c478bd9Sstevel@tonic-gate 	(void) mdb_nv_insert(&mdb.m_nv, "hits", NULL, 0, MDB_NV_PERSIST);
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	(void) mdb_nv_insert(&mdb.m_nv, "thread", &thr_disc, 0,
5347c478bd9Sstevel@tonic-gate 	    MDB_NV_PERSIST | MDB_NV_RDONLY);
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	mdb.m_prsym = mdb_gelf_symtab_create_mutable();
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 	(void) mdb_nv_insert(&mdb.m_modules, mdb.m_pname, NULL,
5397c478bd9Sstevel@tonic-gate 	    (uintptr_t)&mdb.m_rmod, MDB_NV_RDONLY);
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 	for (dcp = &mdb_dcmd_builtins[0]; dcp->dc_name != NULL; dcp++)
5427c478bd9Sstevel@tonic-gate 		(void) mdb_module_add_dcmd(&mdb.m_rmod, dcp, 0);
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	for (i = 0; mdb_dis_builtins[i] != NULL; i++)
5457c478bd9Sstevel@tonic-gate 		(void) mdb_dis_create(mdb_dis_builtins[i]);
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	mdb_macalias_create();
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	mdb_create_builtin_tgts();
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	(void) mdb_callb_add(NULL, MDB_CALLB_PROMPT, (mdb_callb_f)prompt_update,
5527c478bd9Sstevel@tonic-gate 	    NULL);
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate #ifdef _KMDB
5557c478bd9Sstevel@tonic-gate 	(void) mdb_nv_create(&mdb.m_dmodctl, UM_SLEEP);
5567c478bd9Sstevel@tonic-gate #endif
5577c478bd9Sstevel@tonic-gate 	mdb_lex_state_create(&frame0);
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 	mdb_list_append(&mdb.m_flist, &frame0);
5607c478bd9Sstevel@tonic-gate 	mdb.m_frame = &frame0;
5617c478bd9Sstevel@tonic-gate }
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate void
5647c478bd9Sstevel@tonic-gate mdb_destroy(void)
5657c478bd9Sstevel@tonic-gate {
5667c478bd9Sstevel@tonic-gate 	const mdb_dcmd_t *dcp;
5677c478bd9Sstevel@tonic-gate 	mdb_var_t *v;
5687c478bd9Sstevel@tonic-gate 	int unload_mode = MDB_MOD_SILENT;
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate #ifdef _KMDB
5717c478bd9Sstevel@tonic-gate 	unload_mode |= MDB_MOD_DEFER;
5727c478bd9Sstevel@tonic-gate #endif
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	mdb_intr_disable();
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	mdb_macalias_destroy();
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	/*
5797c478bd9Sstevel@tonic-gate 	 * Unload modules _before_ destroying the disassemblers since a
5807c478bd9Sstevel@tonic-gate 	 * module that installs a disassembler should try to clean up after
5817c478bd9Sstevel@tonic-gate 	 * itself.
5827c478bd9Sstevel@tonic-gate 	 */
5837c478bd9Sstevel@tonic-gate 	mdb_module_unload_all(unload_mode);
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 	mdb_nv_rewind(&mdb.m_disasms);
5867c478bd9Sstevel@tonic-gate 	while ((v = mdb_nv_advance(&mdb.m_disasms)) != NULL)
5877c478bd9Sstevel@tonic-gate 		mdb_dis_destroy(mdb_nv_get_cookie(v));
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	mdb_callb_remove_all();
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	if (mdb.m_defdisasm != NULL)
5927c478bd9Sstevel@tonic-gate 		strfree(mdb.m_defdisasm);
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 	if (mdb.m_target != NULL)
5957c478bd9Sstevel@tonic-gate 		(void) mdb_tgt_destroy(mdb.m_target);
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	if (mdb.m_prsym != NULL)
5987c478bd9Sstevel@tonic-gate 		mdb_gelf_symtab_destroy(mdb.m_prsym);
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 	for (dcp = &mdb_dcmd_builtins[0]; dcp->dc_name != NULL; dcp++)
6017c478bd9Sstevel@tonic-gate 		(void) mdb_module_remove_dcmd(&mdb.m_rmod, dcp->dc_name);
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	mdb_nv_destroy(&mdb.m_nv);
6047c478bd9Sstevel@tonic-gate 	mdb_nv_destroy(&mdb.m_walkers);
6057c478bd9Sstevel@tonic-gate 	mdb_nv_destroy(&mdb.m_dcmds);
6067c478bd9Sstevel@tonic-gate 	mdb_nv_destroy(&mdb.m_modules);
6077c478bd9Sstevel@tonic-gate 	mdb_nv_destroy(&mdb.m_disasms);
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	mdb_free(mdb.m_ipathstr, MAXPATHLEN);
6107c478bd9Sstevel@tonic-gate 	mdb_free(mdb.m_lpathstr, MAXPATHLEN);
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	if (mdb.m_ipath != NULL)
6137c478bd9Sstevel@tonic-gate 		mdb_path_free(mdb.m_ipath, mdb.m_ipathlen);
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	if (mdb.m_lpath != NULL)
6167c478bd9Sstevel@tonic-gate 		mdb_path_free(mdb.m_lpath, mdb.m_lpathlen);
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 	if (mdb.m_in != NULL)
6197c478bd9Sstevel@tonic-gate 		mdb_iob_destroy(mdb.m_in);
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	mdb_iob_destroy(mdb.m_out);
6227c478bd9Sstevel@tonic-gate 	mdb.m_out = NULL;
6237c478bd9Sstevel@tonic-gate 	mdb_iob_destroy(mdb.m_err);
6247c478bd9Sstevel@tonic-gate 	mdb.m_err = NULL;
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	if (mdb.m_log != NULL)
6277c478bd9Sstevel@tonic-gate 		mdb_io_rele(mdb.m_log);
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 	mdb_lex_state_destroy(&frame0);
6307c478bd9Sstevel@tonic-gate }
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate /*
6337c478bd9Sstevel@tonic-gate  * The real main loop of the debugger: create a new execution frame on the
6347c478bd9Sstevel@tonic-gate  * debugger stack, and while we have input available, call into the parser.
6357c478bd9Sstevel@tonic-gate  */
6367c478bd9Sstevel@tonic-gate int
6377c478bd9Sstevel@tonic-gate mdb_run(void)
6387c478bd9Sstevel@tonic-gate {
6397c478bd9Sstevel@tonic-gate 	volatile int err;
6407c478bd9Sstevel@tonic-gate 	mdb_frame_t f;
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate 	mdb_intr_disable();
6437c478bd9Sstevel@tonic-gate 	mdb_frame_push(&f);
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	/*
6467c478bd9Sstevel@tonic-gate 	 * This is a fresh mdb context, so ignore any pipe command we may have
6477c478bd9Sstevel@tonic-gate 	 * inherited from the previous frame.
6487c478bd9Sstevel@tonic-gate 	 */
6497c478bd9Sstevel@tonic-gate 	f.f_pcmd = NULL;
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 	if ((err = setjmp(f.f_pcb)) != 0) {
6527c478bd9Sstevel@tonic-gate 		int pop = (mdb.m_in != NULL &&
6537c478bd9Sstevel@tonic-gate 		    (mdb_iob_isapipe(mdb.m_in) || mdb_iob_isastr(mdb.m_in)));
6547c478bd9Sstevel@tonic-gate 		int fromcmd = (f.f_cp != NULL);
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 		mdb_dprintf(MDB_DBG_DSTK, "frame <%u> caught event %s\n",
6577c478bd9Sstevel@tonic-gate 		    f.f_id, mdb_err2str(err));
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 		/*
6607c478bd9Sstevel@tonic-gate 		 * If a syntax error or other failure has occurred, pop all
6617c478bd9Sstevel@tonic-gate 		 * input buffers pushed by commands executed in this frame.
6627c478bd9Sstevel@tonic-gate 		 */
6637c478bd9Sstevel@tonic-gate 		while (mdb_iob_stack_size(&f.f_istk) != 0) {
6647c478bd9Sstevel@tonic-gate 			if (mdb.m_in != NULL)
6657c478bd9Sstevel@tonic-gate 				mdb_iob_destroy(mdb.m_in);
6667c478bd9Sstevel@tonic-gate 			mdb.m_in = mdb_iob_stack_pop(&f.f_istk);
6677c478bd9Sstevel@tonic-gate 			yylineno = mdb_iob_lineno(mdb.m_in);
6687c478bd9Sstevel@tonic-gate 		}
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 		/*
6717c478bd9Sstevel@tonic-gate 		 * Reset standard output and the current frame to a known,
6727c478bd9Sstevel@tonic-gate 		 * clean state, so we can continue execution.
6737c478bd9Sstevel@tonic-gate 		 */
6747c478bd9Sstevel@tonic-gate 		mdb_iob_margin(mdb.m_out, MDB_IOB_DEFMARGIN);
6757c478bd9Sstevel@tonic-gate 		mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
6767c478bd9Sstevel@tonic-gate 		mdb_iob_discard(mdb.m_out);
6777c478bd9Sstevel@tonic-gate 		mdb_frame_reset(&f);
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 		/*
6807c478bd9Sstevel@tonic-gate 		 * If there was an error writing to output, display a warning
6817c478bd9Sstevel@tonic-gate 		 * message if this is the topmost frame.
6827c478bd9Sstevel@tonic-gate 		 */
6837c478bd9Sstevel@tonic-gate 		if (err == MDB_ERR_OUTPUT && mdb.m_depth == 1 && errno != EPIPE)
6847c478bd9Sstevel@tonic-gate 			mdb_warn("write failed");
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 		/*
6877c478bd9Sstevel@tonic-gate 		 * If an interrupt or quit signal is reported, we may have been
6887c478bd9Sstevel@tonic-gate 		 * in the middle of typing or processing the command line:
6897c478bd9Sstevel@tonic-gate 		 * print a newline and discard everything in the parser's iob.
6907c478bd9Sstevel@tonic-gate 		 * Note that we do this after m_out has been reset, otherwise
6917c478bd9Sstevel@tonic-gate 		 * we could trigger a pipe context switch or cause a write
6927c478bd9Sstevel@tonic-gate 		 * to a broken pipe (in the case of a shell command) when
6937c478bd9Sstevel@tonic-gate 		 * writing the newline.
6947c478bd9Sstevel@tonic-gate 		 */
6957c478bd9Sstevel@tonic-gate 		if (err == MDB_ERR_SIGINT || err == MDB_ERR_QUIT) {
6967c478bd9Sstevel@tonic-gate 			mdb_iob_nl(mdb.m_out);
6977c478bd9Sstevel@tonic-gate 			yydiscard();
6987c478bd9Sstevel@tonic-gate 		}
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 		/*
7017c478bd9Sstevel@tonic-gate 		 * If we quit or abort using the output pager, reset the
7027c478bd9Sstevel@tonic-gate 		 * line count on standard output back to zero.
7037c478bd9Sstevel@tonic-gate 		 */
7047c478bd9Sstevel@tonic-gate 		if (err == MDB_ERR_PAGER || MDB_ERR_IS_FATAL(err))
7057c478bd9Sstevel@tonic-gate 			mdb_iob_clearlines(mdb.m_out);
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 		/*
7087c478bd9Sstevel@tonic-gate 		 * If the user requested the debugger quit or abort back to
7097c478bd9Sstevel@tonic-gate 		 * the top, or if standard input is a pipe or mdb_eval("..."),
7107c478bd9Sstevel@tonic-gate 		 * then propagate the error up the debugger stack.
7117c478bd9Sstevel@tonic-gate 		 */
7127c478bd9Sstevel@tonic-gate 		if (MDB_ERR_IS_FATAL(err) || pop != 0 ||
7137c478bd9Sstevel@tonic-gate 		    (err == MDB_ERR_PAGER && mdb.m_fmark != &f) ||
7147c478bd9Sstevel@tonic-gate 		    (err == MDB_ERR_NOMEM && !fromcmd)) {
7157c478bd9Sstevel@tonic-gate 			mdb_frame_pop(&f, err);
7167c478bd9Sstevel@tonic-gate 			return (err);
7177c478bd9Sstevel@tonic-gate 		}
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 		/*
7207c478bd9Sstevel@tonic-gate 		 * If we've returned here from a context where signals were
7217c478bd9Sstevel@tonic-gate 		 * blocked (e.g. a signal handler), we can now unblock them.
7227c478bd9Sstevel@tonic-gate 		 */
7237c478bd9Sstevel@tonic-gate 		if (err == MDB_ERR_SIGINT)
7247c478bd9Sstevel@tonic-gate 			(void) mdb_signal_unblock(SIGINT);
7257c478bd9Sstevel@tonic-gate 	} else
7267c478bd9Sstevel@tonic-gate 		mdb_intr_enable();
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 	for (;;) {
7297c478bd9Sstevel@tonic-gate 		while (mdb.m_in != NULL && (mdb_iob_getflags(mdb.m_in) &
7307c478bd9Sstevel@tonic-gate 		    (MDB_IOB_ERR | MDB_IOB_EOF)) == 0) {
7317c478bd9Sstevel@tonic-gate 			if (mdb.m_depth == 1 &&
7327c478bd9Sstevel@tonic-gate 			    mdb_iob_stack_size(&f.f_istk) == 0) {
7337c478bd9Sstevel@tonic-gate 				mdb_iob_clearlines(mdb.m_out);
7347c478bd9Sstevel@tonic-gate 				mdb_tgt_periodic(mdb.m_target);
7357c478bd9Sstevel@tonic-gate 			}
7367c478bd9Sstevel@tonic-gate 
7377c478bd9Sstevel@tonic-gate 			(void) yyparse();
7387c478bd9Sstevel@tonic-gate 		}
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate 		if (mdb.m_in != NULL) {
7417c478bd9Sstevel@tonic-gate 			if (mdb_iob_err(mdb.m_in)) {
7427c478bd9Sstevel@tonic-gate 				warn("error reading input stream %s\n",
7437c478bd9Sstevel@tonic-gate 				    mdb_iob_name(mdb.m_in));
7447c478bd9Sstevel@tonic-gate 			}
7457c478bd9Sstevel@tonic-gate 			mdb_iob_destroy(mdb.m_in);
7467c478bd9Sstevel@tonic-gate 			mdb.m_in = NULL;
7477c478bd9Sstevel@tonic-gate 		}
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 		if (mdb_iob_stack_size(&f.f_istk) == 0)
7507c478bd9Sstevel@tonic-gate 			break; /* return when we're out of input */
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 		mdb.m_in = mdb_iob_stack_pop(&f.f_istk);
7537c478bd9Sstevel@tonic-gate 		yylineno = mdb_iob_lineno(mdb.m_in);
7547c478bd9Sstevel@tonic-gate 	}
7557c478bd9Sstevel@tonic-gate 
7567c478bd9Sstevel@tonic-gate 	mdb_frame_pop(&f, 0);
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 	/*
7597c478bd9Sstevel@tonic-gate 	 * The value of '.' is a per-frame attribute, to preserve it properly
7607c478bd9Sstevel@tonic-gate 	 * when switching frames.  But in the case of calling mdb_run()
7617c478bd9Sstevel@tonic-gate 	 * explicitly (such as through mdb_eval), we want to propagate the value
7627c478bd9Sstevel@tonic-gate 	 * of '.' to the parent.
7637c478bd9Sstevel@tonic-gate 	 */
7647c478bd9Sstevel@tonic-gate 	mdb_nv_set_value(mdb.m_dot, f.f_dot);
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	return (0);
7677c478bd9Sstevel@tonic-gate }
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate /*
7707c478bd9Sstevel@tonic-gate  * The read-side of the pipe executes this service routine.  We simply call
7717c478bd9Sstevel@tonic-gate  * mdb_run to create a new frame on the execution stack and run the MDB parser,
7727c478bd9Sstevel@tonic-gate  * and then propagate any error code back to the previous frame.
7737c478bd9Sstevel@tonic-gate  */
7747c478bd9Sstevel@tonic-gate static int
7757c478bd9Sstevel@tonic-gate runsvc(void)
7767c478bd9Sstevel@tonic-gate {
7777c478bd9Sstevel@tonic-gate 	int err = mdb_run();
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 	if (err != 0) {
7807c478bd9Sstevel@tonic-gate 		mdb_dprintf(MDB_DBG_DSTK, "forwarding error %s from pipeline\n",
7817c478bd9Sstevel@tonic-gate 		    mdb_err2str(err));
7827c478bd9Sstevel@tonic-gate 		longjmp(mdb.m_frame->f_pcb, err);
7837c478bd9Sstevel@tonic-gate 	}
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 	return (err);
7867c478bd9Sstevel@tonic-gate }
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate /*
7897c478bd9Sstevel@tonic-gate  * Read-side pipe service routine: if we longjmp here, just return to the read
7907c478bd9Sstevel@tonic-gate  * routine because now we have more data to consume.  Otherwise:
7917c478bd9Sstevel@tonic-gate  * (1) if ctx_data is non-NULL, longjmp to the write-side to produce more data;
7927c478bd9Sstevel@tonic-gate  * (2) if wriob is NULL, there is no writer but this is the first read, so we
7937c478bd9Sstevel@tonic-gate  *     can just execute mdb_run() to completion on the current stack;
7947c478bd9Sstevel@tonic-gate  * (3) if (1) and (2) are false, then there is a writer and this is the first
7957c478bd9Sstevel@tonic-gate  *     read, so create a co-routine context to execute mdb_run().
7967c478bd9Sstevel@tonic-gate  */
7977c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7987c478bd9Sstevel@tonic-gate static void
7997c478bd9Sstevel@tonic-gate rdsvc(mdb_iob_t *rdiob, mdb_iob_t *wriob, mdb_iob_ctx_t *ctx)
8007c478bd9Sstevel@tonic-gate {
8017c478bd9Sstevel@tonic-gate 	if (setjmp(ctx->ctx_rpcb) == 0) {
8027c478bd9Sstevel@tonic-gate 		/*
8037c478bd9Sstevel@tonic-gate 		 * Save the current standard input into the pipe context, and
8047c478bd9Sstevel@tonic-gate 		 * reset m_in to point to the pipe.  We will restore it on
8057c478bd9Sstevel@tonic-gate 		 * the way back in wrsvc() below.
8067c478bd9Sstevel@tonic-gate 		 */
8077c478bd9Sstevel@tonic-gate 		ctx->ctx_iob = mdb.m_in;
8087c478bd9Sstevel@tonic-gate 		mdb.m_in = rdiob;
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 		ctx->ctx_rptr = mdb.m_frame;
8117c478bd9Sstevel@tonic-gate 		if (ctx->ctx_wptr != NULL)
8127c478bd9Sstevel@tonic-gate 			mdb_frame_switch(ctx->ctx_wptr);
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate 		if (ctx->ctx_data != NULL)
8157c478bd9Sstevel@tonic-gate 			longjmp(ctx->ctx_wpcb, 1);
8167c478bd9Sstevel@tonic-gate 		else if (wriob == NULL)
8177c478bd9Sstevel@tonic-gate 			(void) runsvc();
8187c478bd9Sstevel@tonic-gate 		else if ((ctx->ctx_data = mdb_context_create(runsvc)) != NULL)
8197c478bd9Sstevel@tonic-gate 			mdb_context_switch(ctx->ctx_data);
8207c478bd9Sstevel@tonic-gate 		else
8217c478bd9Sstevel@tonic-gate 			mdb_warn("failed to create pipe context");
8227c478bd9Sstevel@tonic-gate 	}
8237c478bd9Sstevel@tonic-gate }
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate /*
8267c478bd9Sstevel@tonic-gate  * Write-side pipe service routine: if we longjmp here, just return to the
8277c478bd9Sstevel@tonic-gate  * write routine because now we have free space in the pipe buffer for writing;
8287c478bd9Sstevel@tonic-gate  * otherwise longjmp to the read-side to consume data and create space for us.
8297c478bd9Sstevel@tonic-gate  */
8307c478bd9Sstevel@tonic-gate /*ARGSUSED*/
8317c478bd9Sstevel@tonic-gate static void
8327c478bd9Sstevel@tonic-gate wrsvc(mdb_iob_t *rdiob, mdb_iob_t *wriob, mdb_iob_ctx_t *ctx)
8337c478bd9Sstevel@tonic-gate {
8347c478bd9Sstevel@tonic-gate 	if (setjmp(ctx->ctx_wpcb) == 0) {
8357c478bd9Sstevel@tonic-gate 		ctx->ctx_wptr = mdb.m_frame;
8367c478bd9Sstevel@tonic-gate 		if (ctx->ctx_rptr != NULL)
8377c478bd9Sstevel@tonic-gate 			mdb_frame_switch(ctx->ctx_rptr);
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate 		mdb.m_in = ctx->ctx_iob;
8407c478bd9Sstevel@tonic-gate 		longjmp(ctx->ctx_rpcb, 1);
8417c478bd9Sstevel@tonic-gate 	}
8427c478bd9Sstevel@tonic-gate }
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate /*
8457c478bd9Sstevel@tonic-gate  * Call the current frame's mdb command.  This entry point is used by the
8467c478bd9Sstevel@tonic-gate  * MDB parser to actually execute a command once it has successfully parsed
8477c478bd9Sstevel@tonic-gate  * a line of input.  The command is waiting for us in the current frame.
8487c478bd9Sstevel@tonic-gate  * We loop through each command on the list, executing its dcmd with the
8497c478bd9Sstevel@tonic-gate  * appropriate argument.  If the command has a successor, we know it had
8507c478bd9Sstevel@tonic-gate  * a | operator after it, and so we need to create a pipe and replace
8517c478bd9Sstevel@tonic-gate  * stdout with the pipe's output buffer.
8527c478bd9Sstevel@tonic-gate  */
8537c478bd9Sstevel@tonic-gate int
8547c478bd9Sstevel@tonic-gate mdb_call(uintmax_t addr, uintmax_t count, uint_t flags)
8557c478bd9Sstevel@tonic-gate {
8567c478bd9Sstevel@tonic-gate 	mdb_frame_t *fp = mdb.m_frame;
8577c478bd9Sstevel@tonic-gate 	mdb_cmd_t *cp, *ncp;
8587c478bd9Sstevel@tonic-gate 	mdb_iob_t *iobs[2];
8597c478bd9Sstevel@tonic-gate 	int status, err = 0;
8607c478bd9Sstevel@tonic-gate 	jmp_buf pcb;
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	if (mdb_iob_isapipe(mdb.m_in))
8637c478bd9Sstevel@tonic-gate 		yyerror("syntax error");
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 	mdb_intr_disable();
8667c478bd9Sstevel@tonic-gate 	fp->f_cp = mdb_list_next(&fp->f_cmds);
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	if (flags & DCMD_LOOP)
8697c478bd9Sstevel@tonic-gate 		flags |= DCMD_LOOPFIRST; /* set LOOPFIRST if this is a loop */
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	for (cp = mdb_list_next(&fp->f_cmds); cp; cp = mdb_list_next(cp)) {
8727c478bd9Sstevel@tonic-gate 		if (mdb_list_next(cp) != NULL) {
8737c478bd9Sstevel@tonic-gate 			mdb_iob_pipe(iobs, rdsvc, wrsvc);
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 			mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
8767c478bd9Sstevel@tonic-gate 			mdb.m_in = iobs[MDB_IOB_RDIOB];
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 			mdb_iob_stack_push(&fp->f_ostk, mdb.m_out, 0);
8797c478bd9Sstevel@tonic-gate 			mdb.m_out = iobs[MDB_IOB_WRIOB];
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate 			ncp = mdb_list_next(cp);
8827c478bd9Sstevel@tonic-gate 			mdb_vcb_inherit(cp, ncp);
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 			bcopy(fp->f_pcb, pcb, sizeof (jmp_buf));
8857c478bd9Sstevel@tonic-gate 			ASSERT(fp->f_pcmd == NULL);
8867c478bd9Sstevel@tonic-gate 			fp->f_pcmd = ncp;
8877c478bd9Sstevel@tonic-gate 
888*8a6a72fdSaf 			mdb_frame_set_pipe(fp);
889*8a6a72fdSaf 
8907c478bd9Sstevel@tonic-gate 			if ((err = setjmp(fp->f_pcb)) == 0) {
8917c478bd9Sstevel@tonic-gate 				status = mdb_call_idcmd(cp->c_dcmd, addr, count,
8927c478bd9Sstevel@tonic-gate 				    flags | DCMD_PIPE_OUT, &cp->c_argv,
8937c478bd9Sstevel@tonic-gate 				    &cp->c_addrv, cp->c_vcbs);
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 				ASSERT(mdb.m_in == iobs[MDB_IOB_RDIOB]);
8967c478bd9Sstevel@tonic-gate 				ASSERT(mdb.m_out == iobs[MDB_IOB_WRIOB]);
8977c478bd9Sstevel@tonic-gate 			} else {
8987c478bd9Sstevel@tonic-gate 				mdb_dprintf(MDB_DBG_DSTK, "frame <%u> caught "
8997c478bd9Sstevel@tonic-gate 				    "error %s from pipeline\n", fp->f_id,
9007c478bd9Sstevel@tonic-gate 				    mdb_err2str(err));
9017c478bd9Sstevel@tonic-gate 			}
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 			if (err != 0 || DCMD_ABORTED(status)) {
9047c478bd9Sstevel@tonic-gate 				mdb_iob_setflags(mdb.m_in, MDB_IOB_ERR);
9057c478bd9Sstevel@tonic-gate 				mdb_iob_setflags(mdb.m_out, MDB_IOB_ERR);
9067c478bd9Sstevel@tonic-gate 			} else {
9077c478bd9Sstevel@tonic-gate 				mdb_iob_flush(mdb.m_out);
9087c478bd9Sstevel@tonic-gate 				(void) mdb_iob_ctl(mdb.m_out, I_FLUSH,
9097c478bd9Sstevel@tonic-gate 				    (void *)FLUSHW);
9107c478bd9Sstevel@tonic-gate 			}
9117c478bd9Sstevel@tonic-gate 
912*8a6a72fdSaf 			mdb_frame_clear_pipe(fp);
913*8a6a72fdSaf 
9147c478bd9Sstevel@tonic-gate 			mdb_iob_destroy(mdb.m_out);
9157c478bd9Sstevel@tonic-gate 			mdb.m_out = mdb_iob_stack_pop(&fp->f_ostk);
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 			if (mdb.m_in != NULL)
9187c478bd9Sstevel@tonic-gate 				mdb_iob_destroy(mdb.m_in);
9197c478bd9Sstevel@tonic-gate 
9207c478bd9Sstevel@tonic-gate 			mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
9217c478bd9Sstevel@tonic-gate 			yylineno = mdb_iob_lineno(mdb.m_in);
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 			fp->f_pcmd = NULL;
9247c478bd9Sstevel@tonic-gate 			bcopy(pcb, fp->f_pcb, sizeof (jmp_buf));
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 			if (MDB_ERR_IS_FATAL(err))
9277c478bd9Sstevel@tonic-gate 				longjmp(fp->f_pcb, err);
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 			if (err != 0 || DCMD_ABORTED(status) ||
9307c478bd9Sstevel@tonic-gate 			    mdb_addrvec_length(&ncp->c_addrv) == 0)
9317c478bd9Sstevel@tonic-gate 				break;
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 			addr = mdb_nv_get_value(mdb.m_dot);
9347c478bd9Sstevel@tonic-gate 			count = 1;
9357c478bd9Sstevel@tonic-gate 			flags = 0;
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 		} else {
9387c478bd9Sstevel@tonic-gate 			mdb_intr_enable();
9397c478bd9Sstevel@tonic-gate 			(void) mdb_call_idcmd(cp->c_dcmd, addr, count, flags,
9407c478bd9Sstevel@tonic-gate 			    &cp->c_argv, &cp->c_addrv, cp->c_vcbs);
9417c478bd9Sstevel@tonic-gate 			mdb_intr_disable();
9427c478bd9Sstevel@tonic-gate 		}
9437c478bd9Sstevel@tonic-gate 
9447c478bd9Sstevel@tonic-gate 		fp->f_cp = mdb_list_next(cp);
9457c478bd9Sstevel@tonic-gate 		mdb_cmd_reset(cp);
9467c478bd9Sstevel@tonic-gate 	}
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	/*
9497c478bd9Sstevel@tonic-gate 	 * If our last-command list is non-empty, destroy it.  Then copy the
9507c478bd9Sstevel@tonic-gate 	 * current frame's cmd list to the m_lastc list and reset the frame.
9517c478bd9Sstevel@tonic-gate 	 */
9527c478bd9Sstevel@tonic-gate 	while ((cp = mdb_list_next(&mdb.m_lastc)) != NULL) {
9537c478bd9Sstevel@tonic-gate 		mdb_list_delete(&mdb.m_lastc, cp);
9547c478bd9Sstevel@tonic-gate 		mdb_cmd_destroy(cp);
9557c478bd9Sstevel@tonic-gate 	}
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 	mdb_list_move(&fp->f_cmds, &mdb.m_lastc);
9587c478bd9Sstevel@tonic-gate 	mdb_frame_reset(fp);
9597c478bd9Sstevel@tonic-gate 	mdb_intr_enable();
9607c478bd9Sstevel@tonic-gate 	return (err == 0);
9617c478bd9Sstevel@tonic-gate }
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate uintmax_t
9647c478bd9Sstevel@tonic-gate mdb_dot_incr(const char *op)
9657c478bd9Sstevel@tonic-gate {
9667c478bd9Sstevel@tonic-gate 	uintmax_t odot, ndot;
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	odot = mdb_nv_get_value(mdb.m_dot);
9697c478bd9Sstevel@tonic-gate 	ndot = odot + mdb.m_incr;
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate 	if ((odot ^ ndot) & 0x8000000000000000ull)
9727c478bd9Sstevel@tonic-gate 		yyerror("'%s' would cause '.' to overflow\n", op);
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate 	return (ndot);
9757c478bd9Sstevel@tonic-gate }
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate uintmax_t
9787c478bd9Sstevel@tonic-gate mdb_dot_decr(const char *op)
9797c478bd9Sstevel@tonic-gate {
9807c478bd9Sstevel@tonic-gate 	uintmax_t odot, ndot;
9817c478bd9Sstevel@tonic-gate 
9827c478bd9Sstevel@tonic-gate 	odot = mdb_nv_get_value(mdb.m_dot);
9837c478bd9Sstevel@tonic-gate 	ndot = odot - mdb.m_incr;
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 	if (ndot > odot)
9867c478bd9Sstevel@tonic-gate 		yyerror("'%s' would cause '.' to underflow\n", op);
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	return (ndot);
9897c478bd9Sstevel@tonic-gate }
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate mdb_iwalker_t *
9927c478bd9Sstevel@tonic-gate mdb_walker_lookup(const char *s)
9937c478bd9Sstevel@tonic-gate {
9947c478bd9Sstevel@tonic-gate 	const char *p = strchr(s, '`');
9957c478bd9Sstevel@tonic-gate 	mdb_var_t *v;
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	if (p != NULL) {
9987c478bd9Sstevel@tonic-gate 		size_t nbytes = MIN((size_t)(p - s), MDB_NV_NAMELEN - 1);
9997c478bd9Sstevel@tonic-gate 		char mname[MDB_NV_NAMELEN];
10007c478bd9Sstevel@tonic-gate 		mdb_module_t *mod;
10017c478bd9Sstevel@tonic-gate 
10027c478bd9Sstevel@tonic-gate 		(void) strncpy(mname, s, nbytes);
10037c478bd9Sstevel@tonic-gate 		mname[nbytes] = '\0';
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate 		if ((v = mdb_nv_lookup(&mdb.m_modules, mname)) == NULL) {
10067c478bd9Sstevel@tonic-gate 			(void) set_errno(EMDB_NOMOD);
10077c478bd9Sstevel@tonic-gate 			return (NULL);
10087c478bd9Sstevel@tonic-gate 		}
10097c478bd9Sstevel@tonic-gate 
10107c478bd9Sstevel@tonic-gate 		mod = mdb_nv_get_cookie(v);
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 		if ((v = mdb_nv_lookup(&mod->mod_walkers, ++p)) != NULL)
10137c478bd9Sstevel@tonic-gate 			return (mdb_nv_get_cookie(v));
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate 	} else if ((v = mdb_nv_lookup(&mdb.m_walkers, s)) != NULL)
10167c478bd9Sstevel@tonic-gate 		return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
10177c478bd9Sstevel@tonic-gate 
10187c478bd9Sstevel@tonic-gate 	(void) set_errno(EMDB_NOWALK);
10197c478bd9Sstevel@tonic-gate 	return (NULL);
10207c478bd9Sstevel@tonic-gate }
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate mdb_idcmd_t *
10237c478bd9Sstevel@tonic-gate mdb_dcmd_lookup(const char *s)
10247c478bd9Sstevel@tonic-gate {
10257c478bd9Sstevel@tonic-gate 	const char *p = strchr(s, '`');
10267c478bd9Sstevel@tonic-gate 	mdb_var_t *v;
10277c478bd9Sstevel@tonic-gate 
10287c478bd9Sstevel@tonic-gate 	if (p != NULL) {
10297c478bd9Sstevel@tonic-gate 		size_t nbytes = MIN((size_t)(p - s), MDB_NV_NAMELEN - 1);
10307c478bd9Sstevel@tonic-gate 		char mname[MDB_NV_NAMELEN];
10317c478bd9Sstevel@tonic-gate 		mdb_module_t *mod;
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 		(void) strncpy(mname, s, nbytes);
10347c478bd9Sstevel@tonic-gate 		mname[nbytes] = '\0';
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 		if ((v = mdb_nv_lookup(&mdb.m_modules, mname)) == NULL) {
10377c478bd9Sstevel@tonic-gate 			(void) set_errno(EMDB_NOMOD);
10387c478bd9Sstevel@tonic-gate 			return (NULL);
10397c478bd9Sstevel@tonic-gate 		}
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 		mod = mdb_nv_get_cookie(v);
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate 		if ((v = mdb_nv_lookup(&mod->mod_dcmds, ++p)) != NULL)
10447c478bd9Sstevel@tonic-gate 			return (mdb_nv_get_cookie(v));
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 	} else if ((v = mdb_nv_lookup(&mdb.m_dcmds, s)) != NULL)
10477c478bd9Sstevel@tonic-gate 		return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	(void) set_errno(EMDB_NODCMD);
10507c478bd9Sstevel@tonic-gate 	return (NULL);
10517c478bd9Sstevel@tonic-gate }
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate void
10547c478bd9Sstevel@tonic-gate mdb_dcmd_usage(const mdb_idcmd_t *idcp, mdb_iob_t *iob)
10557c478bd9Sstevel@tonic-gate {
10567c478bd9Sstevel@tonic-gate 	const char *prefix = "", *usage = "";
10577c478bd9Sstevel@tonic-gate 	char name0 = idcp->idc_name[0];
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 	if (idcp->idc_usage != NULL) {
10607c478bd9Sstevel@tonic-gate 		if (idcp->idc_usage[0] == ':') {
10617c478bd9Sstevel@tonic-gate 			if (name0 != ':' && name0 != '$')
10627c478bd9Sstevel@tonic-gate 				prefix = "address::";
10637c478bd9Sstevel@tonic-gate 			else
10647c478bd9Sstevel@tonic-gate 				prefix = "address";
10657c478bd9Sstevel@tonic-gate 			usage = &idcp->idc_usage[1];
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate 		} else if (idcp->idc_usage[0] == '?') {
10687c478bd9Sstevel@tonic-gate 			if (name0 != ':' && name0 != '$')
10697c478bd9Sstevel@tonic-gate 				prefix = "[address]::";
10707c478bd9Sstevel@tonic-gate 			else
10717c478bd9Sstevel@tonic-gate 				prefix = "[address]";
10727c478bd9Sstevel@tonic-gate 			usage = &idcp->idc_usage[1];
10737c478bd9Sstevel@tonic-gate 
10747c478bd9Sstevel@tonic-gate 		} else
10757c478bd9Sstevel@tonic-gate 			usage = idcp->idc_usage;
10767c478bd9Sstevel@tonic-gate 	}
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate 	mdb_iob_printf(iob, "Usage: %s%s %s\n", prefix, idcp->idc_name, usage);
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate 	if (idcp->idc_help != NULL) {
10817c478bd9Sstevel@tonic-gate 		mdb_iob_printf(iob, "%s: try '::help %s' for more "
10827c478bd9Sstevel@tonic-gate 		    "information\n", mdb.m_pname, idcp->idc_name);
10837c478bd9Sstevel@tonic-gate 	}
10847c478bd9Sstevel@tonic-gate }
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate static mdb_idcmd_t *
10877c478bd9Sstevel@tonic-gate dcmd_ndef(const mdb_idcmd_t *idcp)
10887c478bd9Sstevel@tonic-gate {
10897c478bd9Sstevel@tonic-gate 	mdb_var_t *v = mdb_nv_get_ndef(idcp->idc_var);
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	if (v != NULL)
10927c478bd9Sstevel@tonic-gate 		return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate 	return (NULL);
10957c478bd9Sstevel@tonic-gate }
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate static int
10987c478bd9Sstevel@tonic-gate dcmd_invoke(mdb_idcmd_t *idcp, uintptr_t addr, uint_t flags,
10997c478bd9Sstevel@tonic-gate     int argc, const mdb_arg_t *argv, const mdb_vcb_t *vcbs)
11007c478bd9Sstevel@tonic-gate {
11017c478bd9Sstevel@tonic-gate 	int status;
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	mdb_dprintf(MDB_DBG_DCMD, "dcmd %s`%s dot = %lr incr = %llr\n",
11047c478bd9Sstevel@tonic-gate 	    idcp->idc_modp->mod_name, idcp->idc_name, addr, mdb.m_incr);
11057c478bd9Sstevel@tonic-gate 
11067c478bd9Sstevel@tonic-gate 	if ((status = idcp->idc_funcp(addr, flags, argc, argv)) == DCMD_USAGE) {
11077c478bd9Sstevel@tonic-gate 		mdb_dcmd_usage(idcp, mdb.m_err);
11087c478bd9Sstevel@tonic-gate 		goto done;
11097c478bd9Sstevel@tonic-gate 	}
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	while (status == DCMD_NEXT && (idcp = dcmd_ndef(idcp)) != NULL)
11127c478bd9Sstevel@tonic-gate 		status = idcp->idc_funcp(addr, flags, argc, argv);
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate 	if (status == DCMD_USAGE)
11157c478bd9Sstevel@tonic-gate 		mdb_dcmd_usage(idcp, mdb.m_err);
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 	if (status == DCMD_NEXT)
11187c478bd9Sstevel@tonic-gate 		status = DCMD_OK;
11197c478bd9Sstevel@tonic-gate done:
11207c478bd9Sstevel@tonic-gate 	/*
11217c478bd9Sstevel@tonic-gate 	 * If standard output is a pipe and there are vcbs active, we need to
11227c478bd9Sstevel@tonic-gate 	 * flush standard out and the write-side of the pipe.  The reasons for
11237c478bd9Sstevel@tonic-gate 	 * this are explained in more detail in mdb_vcb.c.
11247c478bd9Sstevel@tonic-gate 	 */
11257c478bd9Sstevel@tonic-gate 	if ((flags & DCMD_PIPE_OUT) && (vcbs != NULL)) {
11267c478bd9Sstevel@tonic-gate 		mdb_iob_flush(mdb.m_out);
11277c478bd9Sstevel@tonic-gate 		(void) mdb_iob_ctl(mdb.m_out, I_FLUSH, (void *)FLUSHW);
11287c478bd9Sstevel@tonic-gate 	}
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate 	return (status);
11317c478bd9Sstevel@tonic-gate }
11327c478bd9Sstevel@tonic-gate 
11337c478bd9Sstevel@tonic-gate /*
11347c478bd9Sstevel@tonic-gate  * Call an internal dcmd directly: this code is used by module API functions
11357c478bd9Sstevel@tonic-gate  * that need to execute dcmds, and by mdb_call() above.
11367c478bd9Sstevel@tonic-gate  */
11377c478bd9Sstevel@tonic-gate int
11387c478bd9Sstevel@tonic-gate mdb_call_idcmd(mdb_idcmd_t *idcp, uintmax_t addr, uintmax_t count,
11397c478bd9Sstevel@tonic-gate     uint_t flags, mdb_argvec_t *avp, mdb_addrvec_t *adp, mdb_vcb_t *vcbs)
11407c478bd9Sstevel@tonic-gate {
11417c478bd9Sstevel@tonic-gate 	int is_exec = (strcmp(idcp->idc_name, "$<") == 0);
11427c478bd9Sstevel@tonic-gate 	mdb_arg_t *argv;
11437c478bd9Sstevel@tonic-gate 	int argc;
11447c478bd9Sstevel@tonic-gate 	uintmax_t i;
11457c478bd9Sstevel@tonic-gate 	int status;
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate 	/*
11487c478bd9Sstevel@tonic-gate 	 * Update the values of dot and the most recent address and count
11497c478bd9Sstevel@tonic-gate 	 * to the values of our input parameters.
11507c478bd9Sstevel@tonic-gate 	 */
11517c478bd9Sstevel@tonic-gate 	mdb_nv_set_value(mdb.m_dot, addr);
11527c478bd9Sstevel@tonic-gate 	mdb.m_raddr = addr;
11537c478bd9Sstevel@tonic-gate 	mdb.m_dcount = count;
11547c478bd9Sstevel@tonic-gate 
11557c478bd9Sstevel@tonic-gate 	/*
11567c478bd9Sstevel@tonic-gate 	 * Here the adb(1) man page lies: '9' is only set to count
11577c478bd9Sstevel@tonic-gate 	 * when the command is $<, not when it's $<<.
11587c478bd9Sstevel@tonic-gate 	 */
11597c478bd9Sstevel@tonic-gate 	if (is_exec)
11607c478bd9Sstevel@tonic-gate 		mdb_nv_set_value(mdb.m_rcount, count);
11617c478bd9Sstevel@tonic-gate 
11627c478bd9Sstevel@tonic-gate 	/*
11637c478bd9Sstevel@tonic-gate 	 * We can now return if the repeat count is zero.
11647c478bd9Sstevel@tonic-gate 	 */
11657c478bd9Sstevel@tonic-gate 	if (count == 0)
11667c478bd9Sstevel@tonic-gate 		return (DCMD_OK);
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate 	/*
11697c478bd9Sstevel@tonic-gate 	 * To guard against bad dcmds, we avoid passing the actual argv that
11707c478bd9Sstevel@tonic-gate 	 * we will use to free argument strings directly to the dcmd.  Instead,
11717c478bd9Sstevel@tonic-gate 	 * we pass a copy that will be garbage collected automatically.
11727c478bd9Sstevel@tonic-gate 	 */
11737c478bd9Sstevel@tonic-gate 	argc = avp->a_nelems;
11747c478bd9Sstevel@tonic-gate 	argv = mdb_alloc(sizeof (mdb_arg_t) * argc, UM_SLEEP | UM_GC);
11757c478bd9Sstevel@tonic-gate 	bcopy(avp->a_data, argv, sizeof (mdb_arg_t) * argc);
11767c478bd9Sstevel@tonic-gate 
11777c478bd9Sstevel@tonic-gate 	if (mdb_addrvec_length(adp) != 0) {
11787c478bd9Sstevel@tonic-gate 		flags |= DCMD_PIPE | DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
11797c478bd9Sstevel@tonic-gate 		addr = mdb_addrvec_shift(adp);
11807c478bd9Sstevel@tonic-gate 		mdb_nv_set_value(mdb.m_dot, addr);
11817c478bd9Sstevel@tonic-gate 		mdb_vcb_propagate(vcbs);
11827c478bd9Sstevel@tonic-gate 		count = 1;
11837c478bd9Sstevel@tonic-gate 	}
11847c478bd9Sstevel@tonic-gate 
11857c478bd9Sstevel@tonic-gate 	status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
11867c478bd9Sstevel@tonic-gate 	if (DCMD_ABORTED(status))
11877c478bd9Sstevel@tonic-gate 		goto done;
11887c478bd9Sstevel@tonic-gate 
11897c478bd9Sstevel@tonic-gate 	/*
11907c478bd9Sstevel@tonic-gate 	 * If the command is $< and we're not receiving input from a pipe, we
11917c478bd9Sstevel@tonic-gate 	 * ignore the repeat count and just return since the macro file is now
11927c478bd9Sstevel@tonic-gate 	 * pushed on to the input stack.
11937c478bd9Sstevel@tonic-gate 	 */
11947c478bd9Sstevel@tonic-gate 	if (is_exec && mdb_addrvec_length(adp) == 0)
11957c478bd9Sstevel@tonic-gate 		goto done;
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate 	/*
11987c478bd9Sstevel@tonic-gate 	 * If we're going to loop, we've already executed the dcmd once,
11997c478bd9Sstevel@tonic-gate 	 * so clear the LOOPFIRST flag before proceeding.
12007c478bd9Sstevel@tonic-gate 	 */
12017c478bd9Sstevel@tonic-gate 	if (flags & DCMD_LOOP)
12027c478bd9Sstevel@tonic-gate 		flags &= ~DCMD_LOOPFIRST;
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	for (i = 1; i < count; i++) {
12057c478bd9Sstevel@tonic-gate 		addr = mdb_dot_incr(",");
12067c478bd9Sstevel@tonic-gate 		mdb_nv_set_value(mdb.m_dot, addr);
12077c478bd9Sstevel@tonic-gate 		status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
12087c478bd9Sstevel@tonic-gate 		if (DCMD_ABORTED(status))
12097c478bd9Sstevel@tonic-gate 			goto done;
12107c478bd9Sstevel@tonic-gate 	}
12117c478bd9Sstevel@tonic-gate 
12127c478bd9Sstevel@tonic-gate 	while (mdb_addrvec_length(adp) != 0) {
12137c478bd9Sstevel@tonic-gate 		addr = mdb_addrvec_shift(adp);
12147c478bd9Sstevel@tonic-gate 		mdb_nv_set_value(mdb.m_dot, addr);
12157c478bd9Sstevel@tonic-gate 		mdb_vcb_propagate(vcbs);
12167c478bd9Sstevel@tonic-gate 		status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
12177c478bd9Sstevel@tonic-gate 		if (DCMD_ABORTED(status))
12187c478bd9Sstevel@tonic-gate 			goto done;
12197c478bd9Sstevel@tonic-gate 	}
12207c478bd9Sstevel@tonic-gate done:
12217c478bd9Sstevel@tonic-gate 	mdb_iob_nlflush(mdb.m_out);
12227c478bd9Sstevel@tonic-gate 	return (status);
12237c478bd9Sstevel@tonic-gate }
12247c478bd9Sstevel@tonic-gate 
12257c478bd9Sstevel@tonic-gate void
12267c478bd9Sstevel@tonic-gate mdb_intr_enable(void)
12277c478bd9Sstevel@tonic-gate {
12287c478bd9Sstevel@tonic-gate 	ASSERT(mdb.m_intr >= 1);
12297c478bd9Sstevel@tonic-gate 	if (mdb.m_intr == 1 && mdb.m_pend != 0) {
12307c478bd9Sstevel@tonic-gate 		(void) mdb_signal_block(SIGINT);
12317c478bd9Sstevel@tonic-gate 		mdb.m_intr = mdb.m_pend = 0;
12327c478bd9Sstevel@tonic-gate 		mdb_dprintf(MDB_DBG_DSTK, "delivering pending INT\n");
12337c478bd9Sstevel@tonic-gate 		longjmp(mdb.m_frame->f_pcb, MDB_ERR_SIGINT);
12347c478bd9Sstevel@tonic-gate 	} else
12357c478bd9Sstevel@tonic-gate 		mdb.m_intr--;
12367c478bd9Sstevel@tonic-gate }
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate void
12397c478bd9Sstevel@tonic-gate mdb_intr_disable(void)
12407c478bd9Sstevel@tonic-gate {
12417c478bd9Sstevel@tonic-gate 	mdb.m_intr++;
12427c478bd9Sstevel@tonic-gate 	ASSERT(mdb.m_intr >= 1);
12437c478bd9Sstevel@tonic-gate }
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate /*
12467c478bd9Sstevel@tonic-gate  * Create an encoded string representing the internal user-modifiable
12477c478bd9Sstevel@tonic-gate  * configuration of the debugger and return a pointer to it.  The string can be
12487c478bd9Sstevel@tonic-gate  * used to initialize another instance of the debugger with the same
12497c478bd9Sstevel@tonic-gate  * configuration as this one.
12507c478bd9Sstevel@tonic-gate  */
12517c478bd9Sstevel@tonic-gate char *
12527c478bd9Sstevel@tonic-gate mdb_get_config(void)
12537c478bd9Sstevel@tonic-gate {
12547c478bd9Sstevel@tonic-gate 	size_t r, n = 0;
12557c478bd9Sstevel@tonic-gate 	char *s = NULL;
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate 	while ((r = mdb_snprintf(s, n,
12587c478bd9Sstevel@tonic-gate 	    "%x;%x;%x;%x;%x;%x;%lx;%x;%x;%s;%s;%s;%s;%s",
12597c478bd9Sstevel@tonic-gate 	    mdb.m_tgtflags, mdb.m_flags, mdb.m_debug, mdb.m_radix, mdb.m_nargs,
12607c478bd9Sstevel@tonic-gate 	    mdb.m_histlen, (ulong_t)mdb.m_symdist, mdb.m_execmode,
12617c478bd9Sstevel@tonic-gate 	    mdb.m_forkmode, mdb.m_root, mdb.m_termtype, mdb.m_ipathstr,
12627c478bd9Sstevel@tonic-gate 	    mdb.m_lpathstr, mdb.m_prompt)) > n) {
12637c478bd9Sstevel@tonic-gate 
12647c478bd9Sstevel@tonic-gate 		mdb_free(s, n);
12657c478bd9Sstevel@tonic-gate 		n = r + 1;
12667c478bd9Sstevel@tonic-gate 		s = mdb_alloc(r + 1, UM_SLEEP);
12677c478bd9Sstevel@tonic-gate 	}
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	return (s);
12707c478bd9Sstevel@tonic-gate }
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate /*
12737c478bd9Sstevel@tonic-gate  * Decode a configuration string created with mdb_get_config() and reset the
12747c478bd9Sstevel@tonic-gate  * appropriate parts of the global mdb_t accordingly.
12757c478bd9Sstevel@tonic-gate  */
12767c478bd9Sstevel@tonic-gate void
12777c478bd9Sstevel@tonic-gate mdb_set_config(const char *s)
12787c478bd9Sstevel@tonic-gate {
12797c478bd9Sstevel@tonic-gate 	const char *p;
12807c478bd9Sstevel@tonic-gate 	size_t len;
12817c478bd9Sstevel@tonic-gate 
12827c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
12837c478bd9Sstevel@tonic-gate 		mdb.m_tgtflags = strntoul(s, (size_t)(p - s), 16);
12847c478bd9Sstevel@tonic-gate 		s = p + 1;
12857c478bd9Sstevel@tonic-gate 	}
12867c478bd9Sstevel@tonic-gate 
12877c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
12887c478bd9Sstevel@tonic-gate 		mdb.m_flags = strntoul(s, (size_t)(p - s), 16);
12897c478bd9Sstevel@tonic-gate 		mdb.m_flags &= ~(MDB_FL_LOG | MDB_FL_LATEST);
12907c478bd9Sstevel@tonic-gate 		s = p + 1;
12917c478bd9Sstevel@tonic-gate 	}
12927c478bd9Sstevel@tonic-gate 
12937c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
12947c478bd9Sstevel@tonic-gate 		mdb.m_debug = strntoul(s, (size_t)(p - s), 16);
12957c478bd9Sstevel@tonic-gate 		s = p + 1;
12967c478bd9Sstevel@tonic-gate 	}
12977c478bd9Sstevel@tonic-gate 
12987c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
12997c478bd9Sstevel@tonic-gate 		mdb.m_radix = (int)strntoul(s, (size_t)(p - s), 16);
13007c478bd9Sstevel@tonic-gate 		if (mdb.m_radix < 2 || mdb.m_radix > 16)
13017c478bd9Sstevel@tonic-gate 			mdb.m_radix = MDB_DEF_RADIX;
13027c478bd9Sstevel@tonic-gate 		s = p + 1;
13037c478bd9Sstevel@tonic-gate 	}
13047c478bd9Sstevel@tonic-gate 
13057c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
13067c478bd9Sstevel@tonic-gate 		mdb.m_nargs = (int)strntoul(s, (size_t)(p - s), 16);
13077c478bd9Sstevel@tonic-gate 		mdb.m_nargs = MAX(mdb.m_nargs, 0);
13087c478bd9Sstevel@tonic-gate 		s = p + 1;
13097c478bd9Sstevel@tonic-gate 	}
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
13127c478bd9Sstevel@tonic-gate 		mdb.m_histlen = (int)strntoul(s, (size_t)(p - s), 16);
13137c478bd9Sstevel@tonic-gate 		mdb.m_histlen = MAX(mdb.m_histlen, 1);
13147c478bd9Sstevel@tonic-gate 		s = p + 1;
13157c478bd9Sstevel@tonic-gate 	}
13167c478bd9Sstevel@tonic-gate 
13177c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
13187c478bd9Sstevel@tonic-gate 		mdb.m_symdist = strntoul(s, (size_t)(p - s), 16);
13197c478bd9Sstevel@tonic-gate 		s = p + 1;
13207c478bd9Sstevel@tonic-gate 	}
13217c478bd9Sstevel@tonic-gate 
13227c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
13237c478bd9Sstevel@tonic-gate 		mdb.m_execmode = (uchar_t)strntoul(s, (size_t)(p - s), 16);
13247c478bd9Sstevel@tonic-gate 		if (mdb.m_execmode > MDB_EM_FOLLOW)
13257c478bd9Sstevel@tonic-gate 			mdb.m_execmode = MDB_EM_ASK;
13267c478bd9Sstevel@tonic-gate 		s = p + 1;
13277c478bd9Sstevel@tonic-gate 	}
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
13307c478bd9Sstevel@tonic-gate 		mdb.m_forkmode = (uchar_t)strntoul(s, (size_t)(p - s), 16);
13317c478bd9Sstevel@tonic-gate 		if (mdb.m_forkmode > MDB_FM_CHILD)
13327c478bd9Sstevel@tonic-gate 			mdb.m_forkmode = MDB_FM_ASK;
13337c478bd9Sstevel@tonic-gate 		s = p + 1;
13347c478bd9Sstevel@tonic-gate 	}
13357c478bd9Sstevel@tonic-gate 
13367c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
13377c478bd9Sstevel@tonic-gate 		mdb.m_root = strndup(s, (size_t)(p - s));
13387c478bd9Sstevel@tonic-gate 		s = p + 1;
13397c478bd9Sstevel@tonic-gate 	}
13407c478bd9Sstevel@tonic-gate 
13417c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
13427c478bd9Sstevel@tonic-gate 		mdb.m_termtype = strndup(s, (size_t)(p - s));
13437c478bd9Sstevel@tonic-gate 		s = p + 1;
13447c478bd9Sstevel@tonic-gate 	}
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
13477c478bd9Sstevel@tonic-gate 		size_t len = MIN(sizeof (mdb.m_ipathstr) - 1, p - s);
13487c478bd9Sstevel@tonic-gate 		strncpy(mdb.m_ipathstr, s, len);
13497c478bd9Sstevel@tonic-gate 		mdb.m_ipathstr[len] = '\0';
13507c478bd9Sstevel@tonic-gate 		s = p + 1;
13517c478bd9Sstevel@tonic-gate 	}
13527c478bd9Sstevel@tonic-gate 
13537c478bd9Sstevel@tonic-gate 	if ((p = strchr(s, ';')) != NULL) {
13547c478bd9Sstevel@tonic-gate 		size_t len = MIN(sizeof (mdb.m_lpathstr) - 1, p - s);
13557c478bd9Sstevel@tonic-gate 		strncpy(mdb.m_lpathstr, s, len);
13567c478bd9Sstevel@tonic-gate 		mdb.m_lpathstr[len] = '\0';
13577c478bd9Sstevel@tonic-gate 		s = p + 1;
13587c478bd9Sstevel@tonic-gate 	}
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate 	p = s + strlen(s);
13617c478bd9Sstevel@tonic-gate 	len = MIN(MDB_PROMPTLEN, (size_t)(p - s));
13627c478bd9Sstevel@tonic-gate 	(void) strncpy(mdb.m_prompt, s, len);
13637c478bd9Sstevel@tonic-gate 	mdb.m_prompt[len] = '\0';
13647c478bd9Sstevel@tonic-gate 	mdb.m_promptlen = len;
13657c478bd9Sstevel@tonic-gate }
13667c478bd9Sstevel@tonic-gate 
13677c478bd9Sstevel@tonic-gate mdb_module_t *
13687c478bd9Sstevel@tonic-gate mdb_get_module(void)
13697c478bd9Sstevel@tonic-gate {
13707c478bd9Sstevel@tonic-gate 	if (mdb.m_lmod)
13717c478bd9Sstevel@tonic-gate 		return (mdb.m_lmod);
13727c478bd9Sstevel@tonic-gate 
13737c478bd9Sstevel@tonic-gate 	if (mdb.m_frame && mdb.m_frame->f_cp && mdb.m_frame->f_cp->c_dcmd)
13747c478bd9Sstevel@tonic-gate 		return (mdb.m_frame->f_cp->c_dcmd->idc_modp);
13757c478bd9Sstevel@tonic-gate 
13767c478bd9Sstevel@tonic-gate 	return (NULL);
13777c478bd9Sstevel@tonic-gate }
1378