/*
 * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
 */
#include <lcrash.h>

/* Local flags
 */
#define FREE_FLAG (1 << C_LFLG_SHFT)

extern trace_t *trace_list[];
extern int max_traces;
extern int next_trace;

/* Forward Declaration
 */
void mktrace_usage(command_t *);

/* 
 * mktrace_cmd() -- Command for building a stack backtrace from scratch.
 */
int
mktrace_cmd(command_t *cmd)
{
	int mode;
	kaddr_t i, saddr, sp, pc, fp, ra;
	uint64_t value;
	trace_t *trace; 
	char str[1024], *ptr;
	sframe_t *sf;

	/* List active trace records
	 */
	if (cmd->flags & C_LIST) {
		fprintf(cmd->ofp, "Active trace records:\n\n");
		for (i = 0; i < next_trace; i++) {
			fprintf(cmd->ofp, "  [%d] 0x%lx\n", 
				i, (long unsigned int)trace_list[i]);
		}
		return(0);
	} else if (cmd->nargs == 0) {
		/* Other than with C_LIST, there have to be SOME 
		 * arguments.
		 */
		fprintf(cmd->efp, "Must specify trace information!\n");
		mktrace_usage(cmd);
		return(1);
	}

	/* Free active trace record(s)
	 */
	if (cmd->flags & FREE_FLAG) {
		if (cmd->flags & C_ALL) {
			/* Free all trace records 
			 */
			for (i = 0; i < next_trace; i++) {
				if (trace_list[i]) {
					free_trace_rec(trace_list[i]);
					trace_list[i] = 0;
				}
			}
			next_trace = 0;
			return(0);
		} 
		if (cmd->nargs == 0) {
			fprintf(cmd->efp, "No trace records to free!\n");
			mktrace_usage(cmd);
			return(1);
		}
		for (i = 0; i < cmd->nargs; i++) {
			kl_get_value(cmd->args[i], &mode, 0, &value);
			if (KL_ERROR || (mode != 2)) {
				fprintf(cmd->efp, "Bad trace_t pointer %s\n", 
					cmd->args[i]);
				continue;
			}
			trace = (trace_t *)((uaddr_t)value);
			for (i = 0; i < next_trace; i++) {
				if (trace_list[i] == trace) {
					free_trace_rec(trace_list[i]);
					trace_list[i] = 0;
					break;
				}
			}
			if (i == next_trace) {
				fprintf(cmd->efp, "trace_t pointer %s not "
					"found\n", cmd->args[i]);
			} else if (i == (next_trace - 1)) {
				next_trace--;
			}
		}
		return(0);
	}

	for (i = 0; i < next_trace; i++) {
		if (trace_list[i] == 0) {
			break;
		}
	}
	if (i == max_traces) {
		fprintf(cmd->efp, "Maximum (%d) trace records allocated\n", 
			max_traces);
		return(1);
	}
	trace = (trace_t *)alloc_trace_rec(C_PERM);
	if (!trace) {
		mktrace_usage(cmd);
		return(1);
	}
	GET_VALUE(cmd->args[0], &value);
	if (KL_ERROR) {
		KL_ERROR = KLE_BAD_SADDR;
		free_trace_rec(trace);
		return(1);
	}
	saddr = (kaddr_t)value;
	setup_trace_rec(saddr, 0, C_PERM, trace);
	if (KL_ERROR) {
		free_trace_rec(trace);
		return(1);
	}
	if (cmd->nargs > 1) {
		if (cmd->nargs != 5) {
			free_trace_rec(trace);
			return(1);
		}
		GET_VALUE(cmd->args[1], &value);
		if (KL_ERROR) {
			KL_ERROR = KLE_BAD_SP;
			free_trace_rec(trace);
			return(1);
		}
		sp = (kaddr_t)value; 
		GET_VALUE(cmd->args[2], &value);
		if (KL_ERROR) {
			KL_ERROR = KLE_BAD_PC;
			free_trace_rec(trace);
			return(1);
		}
		pc = (kaddr_t)value;
		GET_VALUE(cmd->args[3], &value);
		if (KL_ERROR) {
			/*
			KL_ERROR = KLE_BAD_FP;
			*/
			free_trace_rec(trace);
			return(1);
		}
		fp = (kaddr_t)fp;
		GET_VALUE(cmd->args[4], &value);
		if (KL_ERROR) {
			/*
			KL_ERROR = KLE_BAD_RA;
			*/
			free_trace_rec(trace);
			return(1);
		}
		ra = (kaddr_t)value;
		/* Set up first frame (which is special because we
		 * have the SP and PC in addition to the FP and RA).
		 */
		sf = (sframe_t *)alloc_sframe(trace, C_PERM);
		sf->sp = sp;
		sf->pc = pc;
		sf->fp = fp;
		sf->ra = ra;
		kl_enqueue((element_t **)&trace->frame, (element_t *)sf);
	}
	while (1) {
		fprintf(cmd->ofp, "Enter FP and RA: ");	
		get_string(str, 1024);
		if (str[0] == 0) {
			break;
		}
		ptr = str;
		while(*ptr != ' ') {
			if (*ptr == 0) {
				return(1);
			}
			ptr++;
		}
		*ptr = 0;
		ptr++;
		GET_VALUE(str, &value);
		fp = (kaddr_t)value;
		GET_VALUE(ptr, &value);
		ra = (kaddr_t)value;
		add_frame(trace, fp, ra);
	}
	finish_trace(trace);
	fprintf(cmd->ofp, "TRACE RECORD CREATED: 0x%lx\n", 
		(unsigned long int)trace);
	trace_list[i] = trace;
	if (i == next_trace) {
		next_trace++;
	}
	return(0);
}



#define _MKTRACE_USAGE "[-l] [-w outfile] [stack_addr SP PC FP RA] | [-F [-a] [free_list]]"

/*
 * mktrace_usage() -- Print the usage string for the 'mktrace' command.
 */
void
mktrace_usage(command_t *cmd)
{
	CMD_USAGE(cmd, _MKTRACE_USAGE);
}

/*
 * mktrace_help() -- Print the help information for the 'mktrace' command.
 */
void
mktrace_help(command_t *cmd)
{
	CMD_HELP(cmd, _MKTRACE_USAGE,
		"Construct a stack backtrace from scratch using an arbitrary "
		"stack_addr, SP, PC, FP, and RA. Alternately, free a trace "
		"record that was previously allocated, list currently "
		"allocated trace records, and delete selected or all active "
		"trace records.");
}

/*
 * mktrace_parse() -- Parse the command line arguments for 'mktrace'.
 */
int
mktrace_parse(command_t *cmd)
{
	option_t *op;
	if (set_cmd_flags(cmd, (C_ALL|C_LIST), "F")) {
		return(1);
	}
	op = cmd->options;
	while (op) {
		switch(op->op_char) {
			case 'F':
				cmd->flags |= FREE_FLAG;
				break;
		}
		op = op->op_next;
	}
	/* Since we will be allocating a perminate trace record, we
	 * need to also set eh C_PERM flag.
	 */
	cmd->flags |= C_PERM;
	return(0);
}
