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

/* Forward declaration
 */
void strace_usage(command_t *);

/* 
 * strace_cmd() -- Command for building stack traces. It either builds
 *                 all possible stack traces (with or without errors)
 *                 contained in a given a stack.  Or, it builds a specific
 *                 backtrace -- using PC, SP and STACK addresses.
 */
int
strace_cmd(command_t *cmd)
{
	int level = 3;
	kaddr_t pc, sp, saddr;
	uint64_t value;
	trace_t *trace; 

	if (cmd->flags & C_LIST) {
		GET_VALUE(cmd->args[0], &value);
		if (KL_ERROR) {
			strace_usage(cmd);
			return(1);
		}
		saddr = (kaddr_t)value;
		do_list(saddr, STACK_SIZE, cmd->ofp);
	} else if (cmd->nargs <= 2) {
		GET_VALUE(cmd->args[0], &value);
		if (KL_ERROR) {
			strace_usage(cmd);
			return(1);
		}
		saddr = (kaddr_t)value;
		if (cmd->nargs == 2) {
			GET_VALUE(cmd->args[1], &value);
			level = (int)value;
		}
		print_traces(saddr, level, cmd->flags, cmd->ofp);
	} else if (KL_ARCH == ARCH_IA64) {

		kaddr_t bsp, cfm, task;

		if (cmd->nargs != 4) {
			strace_usage(cmd);
			return(1);
		}
		GET_VALUE(cmd->args[0], &value);
		if (KL_ERROR) {
			KL_ERROR = KLE_BAD_PC;
			return(1);
		}
		pc = (kaddr_t)value;
		GET_VALUE(cmd->args[1], &value);
		if (KL_ERROR) {
			return(1);
		}
		bsp = (kaddr_t)value;
		GET_VALUE(cmd->args[2], &value);
		if (KL_ERROR) {
			return(1);
		}
		cfm = (kaddr_t)value;
		GET_VALUE(cmd->args[3], &value);
		if (KL_ERROR) {
			return(1);
		}
		task = (kaddr_t)value;
		trace = (trace_t *)alloc_trace_rec(K_TEMP);
		if (!trace) {
			fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
		} else {
			saddr = kl_kernelstack(task);
			setup_trace_rec(saddr, task, 0, trace);
			find_trace(pc, bsp, cfm, 0, trace, 0);
			if (KL_ERROR) {
				fprintf(KL_ERRORFP, 
					"Could not find a valid trace!\n");
				return(1);	
			}
			fprintf(cmd->ofp, "\nPC="); 
			print_kaddr(pc, cmd->ofp, 0);
			fprintf(cmd->ofp, "  BSP="); 
			print_kaddr(bsp, cmd->ofp, 0);
			fprintf(cmd->ofp, "  CFM="); 
			print_kaddr(cfm, cmd->ofp, 0);
			fprintf(cmd->ofp, "\n");
			trace_banner(cmd->ofp);
			print_trace(trace, cmd->flags, cmd->ofp);
			trace_banner(cmd->ofp);
			free_trace_rec(trace);
		}
	} else if (cmd->nargs == 3) {
		GET_VALUE(cmd->args[0], &value);
		if (KL_ERROR) {
			KL_ERROR = KLE_BAD_PC;
			return(1);
		}
		pc = (kaddr_t)value; 
		GET_VALUE(cmd->args[1], &value);
		if (KL_ERROR) {
			KL_ERROR = KLE_BAD_SP;
			return(1);
		}
		sp = (kaddr_t)value;
		GET_VALUE(cmd->args[2], &value);
		if (KL_ERROR) {
			KL_ERROR = KLE_BAD_SADDR;
			return(1);
		}
		saddr = (kaddr_t)value;

		trace = (trace_t *)alloc_trace_rec(K_TEMP);
		if (!trace) {
			fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
			return(1);
		} else {
			setup_trace_rec(saddr, 0, 0, trace);
			find_trace(pc, sp, 0, 0, trace, 0);
			if (KL_ERROR) {
				return(1);	
			}
			fprintf(cmd->ofp, "\nPC="); 
			print_kaddr(pc, cmd->ofp, 0);
			fprintf(cmd->ofp, "  SP="); 
			print_kaddr(sp, cmd->ofp, 0);
			fprintf(cmd->ofp, "  SADDR="); 
			print_kaddr(saddr, cmd->ofp, 0);
			fprintf(cmd->ofp, "\n");
			trace_banner(cmd->ofp);
			print_trace(trace, cmd->flags, cmd->ofp);
			trace_banner(cmd->ofp);
			free_trace_rec(trace);
		}
	} else {
		strace_usage(cmd);
		return(1);
	} 
	return(0);
}

#define _STRACE_USAGE \
	"[-a] [-l] [-f] [-w outfile] [pc sp] stack_addr [level]"

#define _STRACE_USAGE_IA64 \
	"[-f] [-l] [-w outfile] pc bsp pfs task [level]"

#define _STRACE_USAGE_S390 \
	"[-f] [-w outfile] stack_addr [level]"

/*
 * strace_usage() -- Print the usage string for the 'strace' command.
 */
void
strace_usage(command_t *cmd)
{
        switch (KL_ARCH) {
	        case ARCH_S390:
	        case ARCH_S390X:
		        CMD_USAGE(cmd, _STRACE_USAGE_S390);
			break;

	        case ARCH_IA64:
		        CMD_USAGE(cmd, _STRACE_USAGE_IA64);
			break;

	        default:
		        CMD_USAGE(cmd, _STRACE_USAGE);
	}
}

#define _STRACE_HELP_S390 \
        "Displays all complete and unique stack traces (containing level "\
        "or more stack frames) from the stack starting at stack_addr. If a "\
        "level isn't specified, then each stack trace must have at least "\
        "three frames to be considered valid."

#define _STRACE_HELP_IA64 \
        "Displays all complete and unique stack traces (containing level "\
        "or more stack frames) from the stack starting at stack_addr. If a "\
        "level isn't specified, then each stack trace must have at least "\
        "three frames to be considered valid. Alternately, use a specific "\
        "PC, BSP address, and PFS value to generate a stack trace for the "\
        "specified TASK."

#define _STRACE_HELP \
        "Displays all complete and unique stack traces (containing level "\
        "or more stack frames) from the stack starting at stack_addr. If a "\
        "level isn't specified, then each stack trace must have at least "\
        "three frames to be considered valid. Alternately, use a specific "\
        "PC and SP to generate a stack trace from the stack starting at "\
        "stack_addr. Or, when the -l command line option is specified, "\
        "displays a list of all saved return addresses contained in the "\
        "stack starting at stack_addr, along with their location in the "\
        "stack and possibly the name of the function called. Or, if the "\
        "-a option is specified, display ALL traces of level or more frames, "\
        "including invalid traces and duplicate (sub) traces."

/*
 * strace_help() -- Print the help information for the 'strace' command.
 */
void
strace_help(command_t *cmd)
{
        switch (KL_ARCH) {
	        case ARCH_S390:
	        case ARCH_S390X: {
		        CMD_HELP(cmd, _STRACE_USAGE_S390, _STRACE_HELP_S390);
			break;
		}

	        case ARCH_IA64: {
		        CMD_HELP(cmd, _STRACE_USAGE_IA64, _STRACE_HELP_IA64);
			break;
		}

	        default: {
		        CMD_HELP(cmd, _STRACE_USAGE, _STRACE_HELP);
		}
	}
}

/*
 * strace_parse() -- Parse the command line arguments for 'strace'.
 */
int
strace_parse(command_t *cmd)
{
        switch (KL_ARCH) {
	        case ARCH_S390:
	        case ARCH_S390X:
		        if (set_cmd_flags(cmd, (C_FULL|C_WRITE), "")) {
			        return(1);
			}
			break;

	        case ARCH_IA64:
		        if (set_cmd_flags(cmd, (C_FULL|C_LIST|C_WRITE), "")) {
			        return(1);
			}
			break;

	        default:
		        if (set_cmd_flags(cmd, (C_ALL|C_FULL|C_LIST|C_WRITE), 
					  "")) {
			        return(1);
			}
	}
	return(0);
}

/*
 * strace_complete() -- Complete arguments of 'strace' command.
 */
char *
strace_complete(command_t *cmd)
{
	char *ret;

	/* complete standard options (for example, -w option) arguments
	 */
	if ((ret = complete_standard_options(cmd)) != NOT_COMPLETED) {
		return(ret);
	}
	fprintf(cmd->ofp, "\n");
	strace_usage(cmd);
	return(DRAW_NEW_ENTIRE_LINE);
}
