OpenSolaris_b135/lib/efcode/engine/debug.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>

#include <fcode/private.h>
#include <fcode/log.h>

#ifndef DEBUG_LVL
#define	DEBUG_LVL	0
#endif

struct bitab {
	token_t bi_ptr;
	char *bi_name;
	int bi_type;
};

struct bitab *lookup_builtin(token_t);

static int debug_level = DEBUG_LVL;

void
set_interpreter_debug_level(long lvl)
{
	debug_level = lvl;
}

long
get_interpreter_debug_level(void)
{
	return (debug_level);
}

void
output_data_stack(fcode_env_t *env, int msglevel)
{
	int i;

	log_message(msglevel, "( ");
	if (DS > env->ds0) {
		for (i = 0; i < (DS - env->ds0); i++)
			log_message(msglevel, "%llx ",
			    (uint64_t)(env->ds0[i + 1]));
	} else
		log_message(msglevel, "<empty> ");
	log_message(msglevel, ") ");
}

void
output_return_stack(fcode_env_t *env, int show_wa, int msglevel)
{
	int i;
	int anyout = 0;

	log_message(msglevel, "R:( ");
	if (show_wa) {
		log_message(msglevel, "%s ",
		    acf_backup_search(env, (acf_t)WA));
		anyout++;
	}
	if (IP) {
		anyout++;
		log_message(msglevel, "%s ", acf_backup_search(env, IP));
	}
	for (i = (RS - env->rs0) - 1; i > 0; i--) {
		anyout++;
		log_message(msglevel, "%s ",
			    acf_backup_search(env, (acf_t)env->rs0[i+1]));
	}
	if (!anyout)
		log_message(msglevel, "<empty> ");
	log_message(msglevel, ") ");
}

void
dump_comma(fcode_env_t *env, char *type)
{
	xforth_t d;

	if (strcmp(type, "x,") == 0)
		d = peek_xforth(env);
	else
		d = TOS;
	log_message(MSG_FC_DEBUG, "%s %p, %llx\n", type, HERE, (uint64_t)d);
}

static int ndebug_names;
#define	MAXDEBUG_NAMES	10
static char *debug_names[MAXDEBUG_NAMES];

static int ndebug_acfs;
#define	MAXDEBUG_ACFS	10
static acf_t debug_acfs[MAXDEBUG_ACFS];

void
add_debug_acf(fcode_env_t *env, acf_t acf)
{
	int i;

	for (i = 0; i < ndebug_acfs; i++)
		if (acf == debug_acfs[i])
			return;

	if (!within_dictionary(env, acf))
		log_message(MSG_ERROR, "Can't debug builtin\n");
	else if (ndebug_acfs >= MAXDEBUG_ACFS)
		log_message(MSG_ERROR, "Too many debug ACF's\n");
	else {
		debug_acfs[ndebug_acfs++] = acf;
		*LINK_TO_FLAGS(ACF_TO_LINK(acf)) |= FLAG_DEBUG;
	}
}

static void
paren_debug(fcode_env_t *env)
{
	acf_t acf;

	acf = (acf_t)POP(DS);
	if (!within_dictionary(env, acf)) {
		log_message(MSG_INFO, "acf: %llx not in dictionary\n",
		    (uint64_t)acf);
		return;
	}
	if ((acf_t)_ALIGN(acf, token_t) != acf) {
		log_message(MSG_INFO, "acf: %llx not aligned\n",
		    (uint64_t)acf);
		return;
	}
	if (*acf != (token_t)(&do_colon)) {
		log_message(MSG_INFO, "acf: %llx not a colon-def\n",
		    (uint64_t)acf);
		return;
	}
	add_debug_acf(env, acf);
}

static void
debug(fcode_env_t *env)
{
	fstack_t d;
	char *word;
	acf_t acf;

	parse_word(env);
	dollar_find(env);
	d = POP(DS);
	if (d) {
		acf = (acf_t)POP(DS);
		add_debug_acf(env, acf);
	} else if (ndebug_names >= MAXDEBUG_NAMES) {
		log_message(MSG_ERROR, "Too many forward debug words\n");
		two_drop(env);
	} else {
		word = pop_a_duped_string(env, NULL);
		log_message(MSG_INFO, "Forward defined word: %s\n", word);
		debug_names[ndebug_names++] = word;
	}
}

/*
 * Eliminate dups and add vocabulary forth to end if not already on list.
 */
static void
order_to_dict_list(fcode_env_t *env, token_t *order[])
{
	int i, j, norder = 0;

	if (env->current)
		order[norder++] = env->current;
	for (i = env->order_depth; i >= 0; i--) {
		for (j = 0; j < norder && order[j] != env->order[i]; j++)
			;
		if (j == norder)
			order[norder++] = env->order[i];
	}
	for (j = 0; j < norder && order[j] != (token_t *)&env->forth_voc_link;
	    j++)
		;
	if (j == norder)
		order[norder++] = (token_t *)&env->forth_voc_link;
	order[norder] = NULL;
}

static acf_t
search_all_dictionaries(fcode_env_t *env,
    acf_t (*fn)(fcode_env_t *, acf_t, void *),
    void *arg)
{
	token_t *order[MAX_ORDER+1];
	int i;
	token_t *dptr;
	acf_t acf;

	order_to_dict_list(env, order);
	for (i = 0; (dptr = order[i]) != NULL; i++) {
		for (dptr = (token_t *)(*dptr); dptr;
		    dptr = (token_t *)(*dptr))
			if ((acf = (*fn)(env, LINK_TO_ACF(dptr), arg)) != NULL)
				return (acf);
	}
	return (NULL);
}

char *
acf_to_str(acf_t acf)
{
	static char msg[(sizeof (acf) * 2) + 3];

	sprintf(msg, "(%08p)", acf);
	return (msg);
}

char *
get_name_or_acf(token_t *dptr)
{
	char *name;

	if ((name = get_name(dptr)) != NULL)
		return (name);
	return (acf_to_str(LINK_TO_ACF(dptr)));
}

static void
output_acf_name(acf_t acf)
{
	char *name;
	token_t *dptr;
	static int acf_count = 0;

	if (acf == NULL) {
		if (acf_count)
			log_message(MSG_INFO, "\n");
		acf_count = 0;
		return;
	}
	dptr = ACF_TO_LINK(acf);
	if ((name = get_name(dptr)) == NULL)
		name = "<noname>";

	log_message(MSG_INFO, "%24s (%08p)", name, acf);
	if (++acf_count >= 2) {
		log_message(MSG_INFO, "\n");
		acf_count = 0;
	} else
		log_message(MSG_INFO, "    ");
}

static void
dot_debug(fcode_env_t *env)
{
	int i;
	token_t *dptr;

	if (ndebug_names == 0)
		log_message(MSG_INFO, "No forward debug words\n");
	else {
		for (i = 0; i < ndebug_names; i++)
			log_message(MSG_INFO, "%s Forward\n", debug_names[i]);
	}
	if (ndebug_acfs == 0)
		log_message(MSG_INFO, "No debug words\n");
	else {
		for (i = 0; i < ndebug_acfs; i++)
			log_message(MSG_INFO, "%s\n",
			    get_name_or_acf(ACF_TO_LINK(debug_acfs[i])));
	}
}

static void
do_undebug(fcode_env_t *env, char *name)
{
	int i;

	for (i = 0; i < ndebug_names; i++) {
		if (strcmp(debug_names[i], name) == 0) {
			log_message(MSG_INFO, "Undebugging forward word %s\n",
			    name);
			FREE(debug_names[i]);
			for (i++; i < ndebug_names; i++)
				debug_names[i - 1] = debug_names[i];
			ndebug_names--;
			break;
		}
	}
}

static void
undebug(fcode_env_t *env)
{
	fstack_t d;
	acf_t acf;
	flag_t *flagp;
	char *name;
	int i, j;

	parse_word(env);
	two_dup(env);
	dollar_find(env);
	d = POP(DS);
	if (d) {
		acf = (acf_t)POP(DS);
		flagp = LINK_TO_FLAGS(ACF_TO_LINK(acf));
		if ((*flagp & FLAG_DEBUG) == 0)
			log_message(MSG_WARN, "Word not debugged?\n");
		else {
			log_message(MSG_INFO, "Undebugging acf: %p\n", acf);
			*flagp &= ~FLAG_DEBUG;
			for (i = 0; i < ndebug_acfs; i++) {
				if (debug_acfs[i] == acf) {
					for (j = i + 1; j < ndebug_acfs; j++)
						debug_acfs[j-1] = debug_acfs[j];
					ndebug_acfs--;
					break;
				}
			}
		}
	} else
		two_drop(env);
	name = pop_a_string(env, NULL);
	do_undebug(env, name);
}

int
name_is_debugged(fcode_env_t *env, char *name)
{
	int i;

	if (ndebug_names <= 0)
		return (0);
	for (i = 0; i < ndebug_names; i++)
		if (strcmp(debug_names[i], name) == 0)
			return (1);
	return (0);
}

/*
 * This is complicated by being given ACF's to temporary compile words which
 * don't have a header.
 */
int
is_debug_word(fcode_env_t *env, acf_t acf)
{
	flag_t *flagp;
	int i;

	/* check to see if any words are being debugged */
	if (ndebug_acfs == 0)
		return (0);

	/* only words in dictionary can be debugged */
	if (!within_dictionary(env, acf))
		return (0);

	/* check that word has "FLAG_DEBUG" on */
	flagp = LINK_TO_FLAGS(ACF_TO_LINK(acf));
	if ((*flagp & FLAG_DEBUG) == 0)
		return (0);

	/* look in table of debug acf's */
	for (i = 0; i < ndebug_acfs; i++)
		if (debug_acfs[i] == acf)
			return (1);
	return (0);
}

#define	MAX_DEBUG_STACK	100
token_t debug_low[MAX_DEBUG_STACK], debug_high[MAX_DEBUG_STACK];
int debug_prev_level[MAX_DEBUG_STACK];
int debug_curr_level[MAX_DEBUG_STACK];
int ndebug_stack = 0;

void
debug_set_level(fcode_env_t *env, int level)
{
	debug_curr_level[ndebug_stack - 1] = level;
	set_interpreter_debug_level(level);
}

token_t
find_semi_in_colon_def(fcode_env_t *env, acf_t acf)
{
	for (; within_dictionary(env, acf); acf++)
		if (*acf == (token_t)(&semi_ptr))
			return ((token_t)acf);
	return (0);
}

void
check_for_debug_entry(fcode_env_t *env)
{
	int top;

	if (is_debug_word(env, WA) && ndebug_stack < MAX_DEBUG_STACK) {
		top = ndebug_stack++;
		debug_prev_level[top] = get_interpreter_debug_level();
		debug_low[top] = (token_t)WA;
		if (*WA == (token_t)(&do_colon)) {
			debug_high[top] =
			    find_semi_in_colon_def(env, WA);
		} else {
			debug_high[top] = 0;	/* marker... */
		}
		debug_set_level(env, DEBUG_STEPPING);
		output_step_message(env);
	}
}

void
check_for_debug_exit(fcode_env_t *env)
{
	if (ndebug_stack) {
		int top = ndebug_stack - 1;

		if (debug_high[top] == 0) {
			set_interpreter_debug_level(debug_prev_level[top]);
			ndebug_stack--;
		} else if ((token_t)IP >= debug_low[top] &&
		    (token_t)IP <= debug_high[top]) {
			set_interpreter_debug_level(debug_curr_level[top]);
		} else {
			set_interpreter_debug_level(debug_prev_level[top]);
		}
	}
}

void
check_semi_debug_exit(fcode_env_t *env)
{
	if (ndebug_stack) {
		int top = ndebug_stack - 1;

		if ((token_t)(IP - 1) == debug_high[top]) {
			set_interpreter_debug_level(debug_prev_level[top]);
			ndebug_stack--;
		}
	}
}

/*
 * Really entering do_run, since this may be a recursive entry to do_run,
 * we need to set the debug level to what it was previously.
 */
int
current_debug_state(fcode_env_t *env)
{
	if (ndebug_stack) {
		int top = ndebug_stack - 1;
		set_interpreter_debug_level(debug_prev_level[top]);
	}
	return (ndebug_stack);
}

void
clear_debug_state(fcode_env_t *env, int oldstate)
{
	if (ndebug_stack && oldstate <= ndebug_stack) {
		set_interpreter_debug_level(debug_prev_level[oldstate]);
		ndebug_stack = oldstate;
	}
}

void
unbug(fcode_env_t *env)
{
	int i;
	token_t *link;
	flag_t *flag;

	for (i = ndebug_stack - 1; i >= 0; i--) {
		link = ACF_TO_LINK(debug_low[i]);
		flag = LINK_TO_FLAGS(link);
		*flag &= ~FLAG_DEBUG;
	}
	clear_debug_state(env, 0);
}

void
output_vitals(fcode_env_t *env)
{
	log_message(MSG_FC_DEBUG, "IP=%p, *IP=%p, WA=%p, *WA=%p ", IP,
	    (IP ? *IP : 0), WA, (WA ? *WA : 0));
}

int
do_exec_debug(fcode_env_t *env, void *fn)
{
	int dl = debug_level;
	int show_wa = 1;

	if ((dl & (DEBUG_EXEC_DUMP_DS | DEBUG_EXEC_DUMP_RS |
	    DEBUG_EXEC_SHOW_VITALS | DEBUG_EXEC_TRACE | DEBUG_TRACING |
	    DEBUG_STEPPING)) == 0)
		return (0);

	if (dl & DEBUG_STEPPING) {
		dl |= DEBUG_EXEC_DUMP_DS;
	}
	if (dl & (DEBUG_STEPPING | DEBUG_EXEC_TRACE)) {
		log_message(MSG_FC_DEBUG, "%-15s ", acf_to_name(env, WA));
		show_wa = 0;
	}
	if (dl & DEBUG_EXEC_DUMP_DS)
		output_data_stack(env, MSG_FC_DEBUG);
	if (dl & DEBUG_EXEC_DUMP_RS)
		output_return_stack(env, show_wa, MSG_FC_DEBUG);
	if (dl & DEBUG_EXEC_SHOW_VITALS)
		output_vitals(env);
	if (dl & DEBUG_TRACING)
		do_fclib_trace(env, (void *) fn);
	log_message(MSG_FC_DEBUG, "\n");
	if (dl & DEBUG_STEPPING)
		return (do_fclib_step(env));
	return (0);
}

static void
smatch(fcode_env_t *env)
{
	int len;
	char *str, *p;

	if ((str = parse_a_string(env, &len)) == NULL)
		log_message(MSG_INFO, "smatch: no string\n");
	else {
		for (p = (char *)env->base; p < (char *)HERE; p++)
			if (memcmp(p, str, len) == 0)
				log_message(MSG_DEBUG, "%p\n", p);
	}
}

void
check_vitals(fcode_env_t *env)
{
	int i;
	token_t *dptr;

	dptr = env->current;
	if (*dptr && !within_dictionary(env, (uchar_t *)*dptr))
		log_message(MSG_ERROR, "Current: %p outside dictionary\n",
		    *dptr);
	for (i = env->order_depth; i >= 0; i--) {
		dptr = env->order[i];
		if (!dptr)
			continue;
		if (*dptr && !within_dictionary(env, (uchar_t *)*dptr))
			log_message(MSG_ERROR, "Order%d: %p outside"
			    " dictionary\n", i, *dptr);
	}
	if (HERE < env->base || HERE >= env->base + dict_size) {
		log_message(MSG_ERROR, "HERE: %p outside range\n", HERE);
	}
	if (DS < env->ds0 || DS >= &env->ds0[stack_size]) {
		forth_abort(env, "DS: %p outside range\n", DS);
	}
	if (RS < env->rs0 || RS >= &env->rs0[stack_size]) {
		log_message(MSG_ERROR, "RS: %p outside range\n", RS);
		RS = env->rs0;
	}
	if (IP && !within_dictionary(env, IP))
		log_message(MSG_ERROR, "IP: %p outside dictionary\n", IP);
	if (!within_dictionary(env, (void *)env->forth_voc_link))
		log_message(MSG_ERROR, "forth_voc_link: %p outside"
		    " dictionary\n", env->forth_voc_link);
}

static void
dump_table(fcode_env_t *env)
{
	int i;

	for (i = 0; i < MAX_FCODE; i++) {
		if (*(env->table[i].apf) != (token_t)(&f_error)) {
			log_message(MSG_DEBUG, "Token: %4x %32s acf = %8p,"
			    " %8p\n", i, env->table[i].name, env->table[i].apf,
			    *(env->table[i].apf));
		}
	}
	log_message(MSG_DEBUG, "%d FCODES implemented\n", fcode_impl_count);
}

void
verify_usage(fcode_env_t *env)
{
	int i, untested = 0;

	for (i = 0; i < MAX_FCODE; i++) {
		int verify;

		verify = env->table[i].flags & (ANSI_WORD|P1275_WORD);
		if ((verify) &&
#ifdef DEBUG
			(env->table[i].usage == 0) &&
#endif
			(env->table[i].apf)) {
			log_message(MSG_DEBUG,
			    "Untested: %4x %32s acf = %8p, %8p\n", i,
			    env->table[i].name, env->table[i].apf,
			    *(env->table[i].apf));
			untested++;
		}
	}
	if (untested)
		log_message(MSG_DEBUG, "%d untested tokens\n", untested);
}

static void
debugf(fcode_env_t *env)
{
	PUSH(DS, (fstack_t)&debug_level);
}

static void
control(fcode_env_t *env)
{
	PUSH(DS, (fstack_t)&env->control);
}

struct bittab {
	int b_bitval;
	char *b_bitname;
} bittab[] = {
	DEBUG_CONTEXT,		"context",
	DEBUG_BYTELOAD_DS,	"byteload-ds",
	DEBUG_BYTELOAD_RS,	"byteload-rs",
	DEBUG_BYTELOAD_TOKENS,	"byteload-tokens",
	DEBUG_NEW_TOKEN,	"new-token",
	DEBUG_EXEC_TRACE,	"exec-trace",
	DEBUG_EXEC_SHOW_VITALS,	"exec-show-vitals",
	DEBUG_EXEC_DUMP_DS,	"exec-dump-ds",
	DEBUG_EXEC_DUMP_RS,	"exec-dump-rs",
	DEBUG_COMMA,		"comma",
	DEBUG_HEADER,		"header",
	DEBUG_EXIT_WORDS,	"exit-words",
	DEBUG_EXIT_DUMP,	"exit-dump",
	DEBUG_DUMP_TOKENS,	"dump-tokens",
	DEBUG_COLON,		"colon",
	DEBUG_NEXT_VITALS,	"next-vitals",
	DEBUG_VOC_FIND,		"voc-find",
	DEBUG_DUMP_DICT_TOKENS,	"dump-dict-tokens",
	DEBUG_TOKEN_USAGE,	"token-usage",
	DEBUG_DUMP_TOKEN_TABLE,	"dump-token-table",
	DEBUG_SHOW_STACK,	"show-stack",
	DEBUG_SHOW_RS,		"show-rs",
	DEBUG_TRACING,		"tracing",
	DEBUG_TRACE_STACK,	"trace-stack",
	DEBUG_CALL_METHOD,	"call-method",
	DEBUG_ACTIONS,		"actions",
	DEBUG_STEPPING,		"stepping",
	DEBUG_REG_ACCESS,	"reg-access",
	DEBUG_ADDR_ABUSE,	"addr-abuse",
	DEBUG_FIND_FCODE,	"find-fcode",
	DEBUG_UPLOAD,		"upload",
	0
};

void
debug_flags_to_output(fcode_env_t *env, int flags)
{
	int first = 1, i;

	for (i = 0; bittab[i].b_bitval != 0; i++)
		if (bittab[i].b_bitval & flags) {
			if (!first)
				log_message(MSG_INFO, ",");
			first = 0;
			log_message(MSG_INFO, bittab[i].b_bitname);
		}
	if (first)
		log_message(MSG_INFO, "<empty>");
	log_message(MSG_INFO, "\n");
}

static void
dot_debugf(fcode_env_t *env)
{
	debug_flags_to_output(env, debug_level);
}

static void
debugf_qmark(fcode_env_t *env)
{
	debug_flags_to_output(env, 0xffffffff);
}

int
debug_flags_to_mask(char *str)
{
	int flags = 0;
	char *p;
	int i;

	if (isdigit(*str)) {
		if (*str == '0') {
			str++;
			if (*str == 'x' || *str == 'X') {
				sscanf(str + 1, "%x", &flags);
			} else
				sscanf(str, "%o", &flags);
		} else
			sscanf(str, "%d", &flags);
		return (flags);
	}
	if (strcmp(str, "clear") == 0)
		return (0);
	if (strcmp(str, "all") == 0)
		return (0xffffffff & ~DEBUG_STEPPING);
	if (*str) {
		do {
			if (p = strchr(str, ','))
				*p++ = '\0';
			for (i = 0; bittab[i].b_bitname != 0; i++)
				if (strcmp(str, bittab[i].b_bitname) == 0) {
					flags |= bittab[i].b_bitval;
					break;
			}
			if (bittab[i].b_bitname == 0)
				log_message(MSG_WARN,
				    "Unknown debug flag: '%s'\n", str);
			str = p;
		} while (p);
	}
	return (flags);
}

static void
set_debugf(fcode_env_t *env)
{
	char *str;

	str = parse_a_string(env, NULL);
	debug_level = debug_flags_to_mask(str);
}

static acf_t
show_a_word(fcode_env_t *env, acf_t acf, void *arg)
{
	static int nshow_words = 0;

	if (acf == NULL) {
		if (nshow_words > 0) {
			log_message(MSG_DEBUG, "\n");
			nshow_words = 0;
		}
		return (NULL);
	}
	log_message(MSG_DEBUG, "%15s  ", get_name_or_acf(ACF_TO_LINK(acf)));
	nshow_words++;
	if (nshow_words >= 4) {
		log_message(MSG_DEBUG, "\n");
		nshow_words = 0;
	}
	return (NULL);
}

void
words(fcode_env_t *env)
{
	(void) search_all_dictionaries(env, show_a_word, NULL);
	(void) show_a_word(env, NULL, NULL);
}

static acf_t
dump_a_word(fcode_env_t *env, acf_t acf, void *arg)
{
	output_acf_name(acf);
	return (NULL);
}

void
dump_words(fcode_env_t *env)
{
	(void) search_all_dictionaries(env, dump_a_word, NULL);
	output_acf_name(NULL);
}

static void
dump_line(uchar_t *ptr)
{
	uchar_t *byte;
	int i;

	log_message(MSG_INFO, "%p  ", ptr);
	for (i = 0, byte = ptr; i < 16; i++) {
		if (i == 8)
			log_message(MSG_INFO, " ");
		log_message(MSG_INFO, "%02.2x ", *byte++);
	}
	log_message(MSG_INFO, " ");
	for (i = 0, byte = ptr; i < 16; i++, byte++) {
		log_message(MSG_INFO, "%c",
		    ((*byte < 0x20) || (*byte > 0x7f)) ? '.' : *byte);
	}
	log_message(MSG_INFO, "\n");
}

void
dump_dictionary(fcode_env_t *env)
{
	uchar_t *ptr;

	log_message(MSG_INFO, "Dictionary dump: base: %p\n", env->base);
	for (ptr = (uchar_t *)(((long)(env->base)) & ~0xf); ptr < HERE;
	    ptr += 16)
		dump_line(ptr);
}

static char *
acf_to_fcode_name(fcode_env_t *env, acf_t acf)
{
	int i;

	for (i = 0; i < MAX_FCODE; i++)
		if (env->table[i].apf == acf)
			return (env->table[i].name);
	return (NULL);
}

static acf_t
acf_match(fcode_env_t *env, acf_t sacf, void *macf)
{
	if (sacf == (acf_t)macf)
		return (sacf);
	return (NULL);
}

/*
 * Given an ACF, return ptr to name or "unknown" string.
 */
char *
acf_to_name(fcode_env_t *env, acf_t acf)
{
	struct bitab *bip;
	static char name_buf[256];
	uchar_t *p, *np;
	int i, n;

	if (!within_dictionary(env, acf)) {
		if ((bip = lookup_builtin((token_t)acf)) != NULL)
			return (bip->bi_name);
		return (NULL);
	}
	return (get_name_or_acf(ACF_TO_LINK(acf)));
}

int
within_dictionary(fcode_env_t *env, void *addr)
{
	return ((uchar_t *)addr >= env->base &&
	    (uchar_t *)addr < env->base + dict_size);
}

static int
within_word(fcode_env_t *env, acf_t acf, acf_t wacf)
{
	if (acf == wacf || acf + 1 == wacf)
		return (1);
	if (*acf == (token_t)(&do_colon)) {
		do {
			if (acf == wacf)
				return (1);
		} while (*acf++ != (token_t)(&semi_ptr));
	}
	return (0);
}

/*
 * Given an ACF in the middle of a colon definition, search dictionary towards
 * beginning for "colon" acf.  If we find a "semi" acf first, we're not in
 * the middle of a colon-def (temporary execute?).
 */
char *
acf_backup_search(fcode_env_t *env, acf_t acf)
{
	acf_t nacf;
	char *name;

	if ((acf_t)_ALIGN(acf, token_t) == acf && within_dictionary(env, acf)) {
		for (nacf = acf; nacf >= (acf_t)env->base; nacf--)
			if (*nacf == (token_t)(&do_colon) ||
			    *nacf == (token_t)(&semi_ptr))
				break;
		if (nacf >= (acf_t)env->base && *nacf == (token_t)(&do_colon) &&
		    (name = get_name(ACF_TO_LINK(nacf))) != NULL)
			return (name);
	}
	return (acf_to_str(acf));
}

/*
 * Print out current process's C stack using /usr/proc/bin/pstack
 */
void
ctrace(fcode_env_t *env)
{
	char buf[256];
	FILE *fd;

	log_message(MSG_DEBUG, "Interpreter C Stack:\n");
	sprintf(buf, "/usr/proc/bin/pstack %d", getpid());
	if ((fd = popen(buf, "r")) == NULL)
		log_perror(MSG_ERROR, "Can't run: %s", buf);
	else {
		while (fgets(buf, sizeof (buf), fd))
			log_message(MSG_DEBUG, buf);
		fclose(fd);
	}
}

/*
 * Dump data, return stacks, try to unthread forth calling stack.
 */
void
ftrace(fcode_env_t *env)
{
	log_message(MSG_DEBUG, "Forth Interpreter Stacks:\n");
	output_data_stack(env, MSG_DEBUG);
	output_return_stack(env, 1, MSG_DEBUG);
	log_message(MSG_DEBUG, "\n");
}

int in_forth_abort;

/*
 * Handle fatal error, if interactive mode, return to ok prompt.
 */
void
forth_abort(fcode_env_t *env, char *fmt, ...)
{
	va_list ap;
	char msg[256];

	if (in_forth_abort) {
		log_message(MSG_FATAL, "ABORT: abort within forth_abort\n");
		abort();
	}
	in_forth_abort++;

	va_start(ap, fmt);
	vsprintf(msg, fmt, ap);
	log_message(MSG_ERROR, "ABORT: %s\n", msg);

	if (env) {
		ctrace(env);
		ftrace(env);
	}

	return_to_interact(env);
	/*
	 * If not in interactive mode, return_to_interact just returns.
	 */
	exit(1);
}

/*
 * Handle fatal system call error
 */
void
forth_perror(fcode_env_t *env, char *fmt, ...)
{
	va_list ap;
	char msg[256];
	int save_errno = errno;	/* just in case... */

	va_start(ap, fmt);
	vsprintf(msg, fmt, ap);

	forth_abort(env, "%s: %s", msg, strerror(save_errno));
}

static void
show_stack(fcode_env_t *env)
{
#ifdef DEBUG
	debug_level ^= DEBUG_SHOW_STACK;
#else
	/*EMPTY*/
#endif
}

static void
print_bytes_header(int width, int offset)
{
	int i;

	for (i = 0; i < width; i++)
		log_message(MSG_INFO, " ");
	log_message(MSG_INFO, "  ");
	for (i = 0; i < 16; i++) {
		if (i == 8)
			log_message(MSG_INFO, " ");
		if (i == offset)
			log_message(MSG_INFO, "\\/ ");
		else
			log_message(MSG_INFO, "%2x ", i);
	}
	log_message(MSG_INFO, " ");
	for (i = 0; i < 16; i++) {
		if (i == offset)
			log_message(MSG_INFO, "v");
		else
			log_message(MSG_INFO, "%x", i);
	}
	log_message(MSG_INFO, "\n");
}

static void
dump(fcode_env_t *env)
{
	uchar_t *data;
	int len, offset;
	char buf[20];

	len = POP(DS);
	data = (uchar_t *)POP(DS);
	offset = ((long)data) & 0xf;
	len += offset;
	data = (uchar_t *)((long)data & ~0xf);
	sprintf(buf, "%p", data);
	print_bytes_header(strlen(buf), offset);
	for (len += offset; len > 0; len -= 16, data += 16)
		dump_line(data);
}

static acf_t
do_sifting(fcode_env_t *env, acf_t acf, void *pat)
{
	char *name;

	if ((name = get_name(ACF_TO_LINK(acf))) != NULL && strstr(name, pat))
		output_acf_name(acf);
	return (NULL);
}

static void
sifting(fcode_env_t *env)
{
	char *pat;

	if ((pat = parse_a_string(env, NULL)) != NULL) {
		(void) search_all_dictionaries(env, do_sifting, pat);
		output_acf_name(NULL);
	}
}

void
print_level(int level, int *doprint)
{
	int i;

	if (*doprint) {
		log_message(MSG_DEBUG, "\n    ");
		for (i = 0; i < level; i++)
			log_message(MSG_DEBUG, "    ");
		*doprint = 0;
	}
}

#define	BI_QUOTE	1
#define	BI_BLIT		2
#define	BI_BDO		3
#define	BI_QDO		4
#define	BI_BR		5
#define	BI_QBR		6
#define	BI_BOF		7
#define	BI_LOOP		8
#define	BI_PLOOP	9
#define	BI_TO		10
#define	BI_SEMI		11
#define	BI_COLON	12
#define	BI_NOOP		13
#define	BI_NOTYET	14	/* unimplented in "see" */

struct bitab bitab[] = {
	(token_t)(&quote_ptr),			"\"",		BI_QUOTE,
	(token_t)(&blit_ptr),			"blit",		BI_BLIT,
	(token_t)(&do_bdo_ptr),			"do",		BI_BDO,
	(token_t)(&do_bqdo_ptr),		"?do",		BI_QDO,
	(token_t)(&bbranch_ptrs[0]),		"br",		BI_BR,
	(token_t)(&bbranch_ptrs[1]),		"qbr",		BI_QBR,
	(token_t)(&bbranch_ptrs[2]),		"bof",		BI_BOF,
	(token_t)(&do_loop_ptr),		"loop",		BI_LOOP,
	(token_t)(&do_ploop_ptr),		"+loop",	BI_PLOOP,
	(token_t)(&to_ptr),			"to",		BI_NOOP,
	(token_t)(&semi_ptr),			";",		BI_SEMI,
	(token_t)(&do_colon),			":",		BI_COLON,
	(token_t)(&tlit_ptr),			"[']",		BI_NOOP,
	(token_t)(&do_leave_ptr),		"leave",	BI_NOTYET,
	(token_t)(&create_ptr),			"create",	BI_NOTYET,
	(token_t)(&does_ptr),			"does>",	BI_NOTYET,
	(token_t)(&value_defines[0][0]),	"a.@",		BI_NOTYET,
	(token_t)(&value_defines[0][1]),	"a.!",		BI_NOTYET,
	(token_t)(&value_defines[0][2]),	"a.nop",	BI_NOTYET,
	(token_t)(&value_defines[1][0]),	"a.i@",		BI_NOTYET,
	(token_t)(&value_defines[1][1]),	"a.i!",		BI_NOTYET,
	(token_t)(&value_defines[1][2]),	"a.iad",	BI_NOTYET,
	(token_t)(&value_defines[2][0]),	"a.defer",	BI_NOTYET,
	(token_t)(&value_defines[2][1]),	"a.@",		BI_NOTYET,
	(token_t)(&value_defines[2][2]),	"a.nop",	BI_NOTYET,
	(token_t)(&value_defines[3][0]),	"a.defexec",	BI_NOTYET,
	(token_t)(&value_defines[3][1]),	"a.iset",	BI_NOTYET,
	(token_t)(&value_defines[3][2]),	"a.iad",	BI_NOTYET,
	(token_t)(&value_defines[4][0]),	"a.binit",	BI_NOTYET,
	(token_t)(&value_defines[4][1]),	"a.2drop",	BI_NOTYET,
	(token_t)(&value_defines[4][2]),	"a.nop",	BI_NOTYET,
	(token_t)(&value_defines[5][0]),	"a.ibinit",	BI_NOTYET,
	(token_t)(&value_defines[5][1]),	"a.2drop",	BI_NOTYET,
	(token_t)(&value_defines[5][2]),	"a.iad",	BI_NOTYET,
	0
};

struct bitab *
lookup_builtin(token_t builtin)
{
	int i;

	for (i = 0; bitab[i].bi_ptr; i++)
		if (bitab[i].bi_ptr == builtin)
			return (&bitab[i]);
	return (NULL);
}

static void
paren_see(fcode_env_t *env)
{
	acf_t save_acf = (acf_t)POP(DS);
	acf_t acf = save_acf;
	int i, n, pass;
	token_t brtab[30], thentab[30], brstk[30];
	int nbrtab = 0, nthentab = 0, nbrstk = 0;
	uchar_t *p;
	int level = 0, doprintlevel = 1, nthen;
	struct bitab *bip;
	token_t last_lit = 0, case_lit = 0, endof_loc = 0, endcase_loc = 0;

	if ((bip = lookup_builtin(*acf)) == NULL ||
	    bip->bi_type != BI_COLON) {
		if (bip = lookup_builtin((token_t)acf))
			log_message(MSG_INFO, "%s: builtin\n", bip->bi_name);
		else
			log_message(MSG_INFO, "%s: builtin\n",
			    acf_to_name(env, acf));
		return;
	}
	log_message(MSG_INFO, ": %s", acf_to_name(env, acf));
	for (pass = 0; pass < 2; pass++) {
		acf = save_acf;
		for (acf++; ; acf++) {
			if (pass) {
				print_level(level, &doprintlevel);
				for (nthen = 0; nthentab > 0 &&
				    thentab[nthentab-1] == (token_t)acf;
				    nthentab--)
					nthen++;
				if (nthen) {
					level -= nthen;
					doprintlevel = 1;
					print_level(level, &doprintlevel);
					for (i = 0; i < nthen; i++)
						log_message(MSG_INFO, "then ");
				}
				print_level(level, &doprintlevel);
				for (i = 0; i < nbrtab; i += 2)
					if ((token_t)acf == brtab[i]) {
						log_message(MSG_INFO, "begin ");
						brstk[nbrstk++] = brtab[i+1];
						level++;
						doprintlevel = 1;
					}
				print_level(level, &doprintlevel);
				if (case_lit == (token_t)acf) {
					log_message(MSG_INFO, "case ");
					doprintlevel = 1;
					print_level(level, &doprintlevel);
				}
				if (endof_loc == (token_t)acf) {
					log_message(MSG_INFO, "endof ");
					doprintlevel = 1;
					print_level(level, &doprintlevel);
				}
				if (endcase_loc == (token_t)acf) {
					doprintlevel = 1;
					print_level(level, &doprintlevel);
					log_message(MSG_INFO, "endcase ");
				}
			}
			if ((bip = lookup_builtin((token_t)*acf)) == 0) {
				last_lit = (token_t)acf;
				if (pass)
					log_message(MSG_INFO, "%s ",
					    acf_to_name(env, (acf_t)*acf));
				continue;
			}
			if (bip->bi_type == BI_SEMI) {
				if (pass) {
					log_message(MSG_INFO, "\n");
					log_message(MSG_INFO, "%s\n",
					    bip->bi_name);
				}
				break;
			}
			switch (bip->bi_type) {

			case BI_NOOP:
			case BI_NOTYET:
				if (pass)
					log_message(MSG_INFO, "%s ",
					    bip->bi_name);
				break;

			case BI_QUOTE:
				if (pass)
					log_message(MSG_INFO, "\" ");
				acf++;
				p = (uchar_t *)acf;
				n = *p++;
				if (pass)
					log_message(MSG_INFO, "%s\" ", p);
				p += n + 1;
				for (; ((token_t)(p)) & (sizeof (token_t) - 1);
				    p++)
					;
				acf = (acf_t)p;
				acf--;
				break;

			case BI_BLIT:
				acf++;
				if (pass)
					log_message(MSG_INFO, "%x ", *acf);
				break;

			case BI_BDO:
			case BI_QDO:
				if (pass) {
					log_message(MSG_INFO, "%s ",
					    bip->bi_name);
					doprintlevel = 1;
					level++;
				}
				acf++;
				break;

			case BI_BR:
				acf++;
				if (pass) {
					if (*acf < (token_t)acf) {
						if (nbrstk) {
							doprintlevel = 1;
							level--;
							print_level(level,
							    &doprintlevel);
							log_message(MSG_INFO,
							    "repeat ");
							nbrstk--;
						} else
							log_message(MSG_INFO,
							    "[br back?]");
					} else if (nthentab) {
						doprintlevel = 1;
						print_level(level - 1,
						    &doprintlevel);
						log_message(MSG_INFO, "else ");
						doprintlevel = 1;
						thentab[nthentab - 1] = *acf;
					}
				} else {
					if (*acf < (token_t)acf) {
						brtab[nbrtab++] = *acf;
						brtab[nbrtab++] = (token_t)acf;
					}
					if (endcase_loc == 0 &&
					    case_lit) {
						endcase_loc = *acf;
					}
				}
				break;

			case BI_QBR:
				acf++;
				if (pass) {
					if (*acf < (token_t)acf) {
						if (nbrstk) {
							doprintlevel = 1;
							level--;
							print_level(level,
							    &doprintlevel);
							log_message(MSG_INFO,
							    "until ");
							nbrstk--;
						} else
							log_message(MSG_INFO,
							    "[br back?]");
					} else if (nbrstk > 0 &&
					    *acf >= brstk[nbrstk - 1]) {
						doprintlevel = 1;
						print_level(level - 1,
						    &doprintlevel);
						log_message(MSG_INFO,
						    "while ");
						doprintlevel = 1;
					} else {
						log_message(MSG_INFO, "if ");
						doprintlevel = 1;
						level++;
						thentab[nthentab++] = *acf;
					}
				} else if (*acf < (token_t)acf) {
					brtab[nbrtab++] = *acf;
					brtab[nbrtab++] = (token_t)acf;
				}
				break;

			case BI_BOF:
				acf++;
				if (pass) {
					log_message(MSG_INFO, "of ");
					endof_loc = *acf;
				} else if (case_lit == 0) {
					case_lit = last_lit;
				}
				break;

			case BI_LOOP:
			case BI_PLOOP:
				if (pass) {
					level--;
					doprintlevel = 1;
					print_level(level, &doprintlevel);
					log_message(MSG_INFO, "%s ",
					    bip->bi_name);
				}
				acf++;
				break;

			default:
				log_message(MSG_ERROR, "Invalid builtin %s\n",
				    bip->bi_name);
			}
		}
	}
}

static void
see(fcode_env_t *env)
{
	fstack_t d;

	parse_word(env);
	dollar_find(env);
	d = POP(DS);
	if (d)
		paren_see(env);
	else {
		log_message(MSG_WARN, "?");
		two_drop(env);
	}
}

static acf_t
do_dot_calls(fcode_env_t *env, acf_t acf, void *cacf)
{
	token_t *dptr = ACF_TO_LINK(acf);
	token_t *wptr = acf;

	if (*wptr == (token_t)(&do_colon)) {
		do {
			if ((acf_t)(*wptr) == (acf_t)cacf)
				output_acf_name(acf);
		} while (*wptr++ != (token_t)(&semi_ptr));
	} else if ((acf_t)(*wptr) == cacf)
		output_acf_name(acf);
	else if (wptr == (token_t *)cacf)
		output_acf_name(acf);
	return (NULL);
}

static void
dot_calls(fcode_env_t *env)
{
	acf_t acf = (acf_t)POP(DS);

	search_all_dictionaries(env, do_dot_calls, acf);
	output_acf_name(NULL);
}

static void
dot_pci_space(fcode_env_t *env)
{
	fstack_t d = POP(DS);

	switch ((d >> 24) & 0x3) {
	case 0: log_message(MSG_INFO, "Config,"); break;
	case 1: log_message(MSG_INFO, "IO,"); break;
	case 2: log_message(MSG_INFO, "Memory32,"); break;
	case 3: log_message(MSG_INFO, "Memory64,"); break;
	}
	if (d & 0x80000000)
		log_message(MSG_INFO, "Not_reloc,");
	if (d & 0x400000000)
		log_message(MSG_INFO, "Prefetch,");
	if (d & 0x200000000)
		log_message(MSG_INFO, "Alias,");
	log_message(MSG_INFO, "Bus%d,", (d >> 16) & 0xff);
	log_message(MSG_INFO, "Dev%d,", (d >> 11) & 0x1f);
	log_message(MSG_INFO, "Func%d,", (d >> 8) & 0x7);
	log_message(MSG_INFO, "Reg%x", d & 0xff);
	log_message(MSG_INFO, "\n");
}

void
fcode_debug(fcode_env_t *env)
{
	PUSH(DS, (fstack_t)(&env->fcode_debug));
}

static void
base_addr(fcode_env_t *env)
{
	PUSH(DS, (fstack_t)env->base);
}

static int mw_valid;
static int mw_size;
static void *mw_addr;
static fstack_t mw_value;
static fstack_t mw_lastvalue;

static fstack_t
mw_fetch(void)
{
	switch (mw_size) {
	case 1: return (*((uint8_t *)mw_addr));
	case 2: return (*((uint16_t *)mw_addr));
	case 4: return (*((uint32_t *)mw_addr));
	case 8: return (*((uint64_t *)mw_addr));
	}
	return (0);
}

void
do_memory_watch(fcode_env_t *env)
{
	fstack_t value;

	if (!mw_valid)
		return;
	value = mw_fetch();
	if (value != mw_lastvalue) {
		if (mw_valid == 1 || mw_value == value) {
			log_message(MSG_INFO,
			    "memory-watch: %p/%d: %llx -> %llx\n",
			    mw_addr, mw_size, (uint64_t)mw_lastvalue,
			    (uint64_t)value);
			do_fclib_step(env);
		}
		mw_lastvalue = value;
	}
}

static void
set_memory_watch(fcode_env_t *env, int type, int size, void *addr,
    fstack_t value)
{
	switch (size) {
	case 1: case 2: case 4: case 8:
		break;
	default:
		log_message(MSG_ERROR, "set_memory_watch: invalid size: %d\n",
		    size);
		return;
	}
	mw_valid = type;
	mw_size = size;
	mw_addr = addr;
	mw_value = value;
	mw_lastvalue = mw_fetch();
}

static void
memory_watch(fcode_env_t *env)
{
	int size = POP(DS);
	void *addr = (void *)POP(DS);

	set_memory_watch(env, 1, size, addr, 0);
}

static void
memory_watch_value(fcode_env_t *env)
{
	int size = POP(DS);
	void *addr = (void *)POP(DS);
	fstack_t value = POP(DS);

	set_memory_watch(env, 2, size, addr, value);
}

static void
memory_watch_clear(fcode_env_t *env)
{
	mw_valid = 0;
}

static void
vsearch(fcode_env_t *env)
{
	fstack_t value;
	int size = POP(DS);
	fstack_t match_value = POP(DS);
	uchar_t *toaddr = (uchar_t *)POP(DS);
	uchar_t *fromaddr = (uchar_t *)POP(DS);

	log_message(MSG_INFO, "%p to %p by %d looking for %llx\n", fromaddr,
	    toaddr, size, (uint64_t)match_value);
	for (; fromaddr < toaddr; fromaddr += size) {
		switch (size) {
		case 1: value = *((uint8_t *)fromaddr); break;
		case 2: value = *((uint16_t *)fromaddr); break;
		case 4: value = *((uint32_t *)fromaddr); break;
		case 8: value = *((uint64_t *)fromaddr); break;
		default:
			log_message(MSG_INFO, "Invalid size: %d\n", size);
			return;
		}
		if (value == match_value)
			log_message(MSG_INFO, "%p\n", fromaddr);
	}
}

#pragma init(_init)

static void
_init(void)
{
	fcode_env_t *env = initial_env;

	ASSERT(env);
	NOTICE;

	FORTH(IMMEDIATE,	"words",		words);
	FORTH(IMMEDIATE,	"dump-words",		dump_words);
	FORTH(IMMEDIATE,	"dump-dict",		dump_dictionary);
	FORTH(IMMEDIATE,	"dump-table",		dump_table);
	FORTH(0,		"debugf",		debugf);
	FORTH(0,		".debugf",		dot_debugf);
	FORTH(0,		"set-debugf",		set_debugf);
	FORTH(0,		"debugf?",		debugf_qmark);
	FORTH(0,		"control",		control);
	FORTH(0,		"dump",			dump);
	FORTH(IMMEDIATE,	"showstack",		show_stack);
	FORTH(IMMEDIATE,	"sifting",		sifting);
	FORTH(IMMEDIATE,	"ctrace",		ctrace);
	FORTH(IMMEDIATE,	"ftrace",		ftrace);
	FORTH(0,		"see",			see);
	FORTH(0,		"(see)",		paren_see);
	FORTH(0,		"base-addr",		base_addr);
	FORTH(0,		"smatch",		smatch);
	FORTH(0,		".calls",		dot_calls);
	FORTH(0,		".pci-space",		dot_pci_space);
	FORTH(0,		"(debug)",		paren_debug);
	FORTH(0,		"debug",		debug);
	FORTH(0,		".debug",		dot_debug);
	FORTH(0,		"undebug",		undebug);
	FORTH(0,		"memory-watch",		memory_watch);
	FORTH(0,		"memory-watch-value",	memory_watch_value);
	FORTH(0,		"memory-watch-clear",	memory_watch_clear);
	FORTH(0,		"vsearch",		vsearch);
}