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

/* Local define
 */
#define C_TFLAG (1 << C_LFLG_SHFT)

#define TRACE_MAX 10
trace_t *trace_list[TRACE_MAX] = { 0,0,0,0,0,0,0,0,0,0, };
int max_traces = TRACE_MAX;
int next_trace = 0;

/*
 * trace_cmd() -- This is the basic 'trace' command.
 */
int
trace_cmd(command_t *cmd)
{
	int i, j, mode, trace_cnt, trace_errors;
	kaddr_t addr;
	uint64_t value;
	syment_t *symp;
	kaddr_t saved_task = 0;
	trace_t *trace;

	trace_cnt = 0;
	trace_errors = 0;

	if (cmd->flags & C_TFLAG) {
		if (cmd->flags & C_ALL) {
			for (j = 0; j < max_traces; j++) {
				if (trace_list[j]) {
					trace_banner(cmd->ofp);
					print_trace(trace_list[j],
						cmd->flags, cmd->ofp);
					trace_cnt++;
				}
			}
		} else if (cmd->nargs) {
			for (i = 0; i < cmd->nargs; i++) {
				kl_get_value(cmd->args[i], &mode, 0, &value);
				if (KL_ERROR) {
					fprintf(KL_ERRORFP, "Could not "
						"generate a trace for task "
						"record %s\n", cmd->args[i]);
					continue;
				}
				trace = (trace_t *)((uaddr_t)value);
				for (j = 0; j < max_traces; j++) {
					if (trace_list[j] == trace) {
						break;
					}
				}
				trace_banner(cmd->ofp);
				if (j == max_traces) {
					fprintf(cmd->ofp, "trace record %s "
						"not found\n", cmd->args[i]);
					trace_errors++;
					continue;
				}
				trace_cnt++;
				print_trace(trace, cmd->flags, cmd->ofp);
			}
		} else {
			return(1);
		}
		if (trace_cnt || trace_errors) {
			trace_banner(cmd->ofp);
		}
		return(0);
	}

	/* Make sure we save the current deftask (even if it is NULL)
	 * so that we can restore it when we are done (if it was modified).
	 */
	saved_task = deftask;

	if (cmd->nargs == 0) {
		if (cmd->flags & C_ALL) {
			kaddr_t first_task;
			struct task_struct *tsp;

			if (!(tsp = (struct task_struct *)
				kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) {
				return(1);
			}

			if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
				symp = kl_lkup_symname("init_task_union");
				addr = symp->s_addr; 
			} else {
				symp = kl_lkup_symname("init_tasks");
				GET_BLOCK(symp->s_addr, sizeof(kaddr_t), &addr);
			}
			if (symp) {
				first_task = addr;
				do {
					if (kl_get_task_struct(addr, 2, tsp)) {
						break;
					}
					if (!task_trace(addr, cmd->flags, 
							cmd->ofp)) {
						trace_cnt++;
					}
					if (KL_TYPEINFO()) {
						addr = kl_kaddr(tsp, 
							"task_struct", 
							"next_task");
					} else {
						addr = kl_kaddr(tsp, 
							"task_struct", 
							"next_task");
					}
				} while (addr != first_task);
				kl_free_block(tsp);
			}
		} else if (deftask) {
			if (task_trace(deftask, cmd->flags, cmd->ofp)) {
				  return(1);
			}            
			trace_cnt++;
		} else if (MIP->core_type == dev_kmem) {
			/* It's a live system, there isn't a deftask, and 
			 * there has been no panic -- there's nothing much 
			 * that can be done but print an error and return.
			 */
			fprintf(KL_ERRORFP, "System is ACTIVE. Set deftask.\n");
			kl_set_deftask(saved_task);
			return(1);
		} else {
			/* Generate a trace for the task that generated the 
			 * core dump.  
			 */
			if ((addr = kl_dumptask())) {
				kl_set_deftask(addr);
				if (task_trace(deftask, cmd->flags, cmd->ofp)) {
					  return(1);
				}            
				trace_cnt++;
			}
		}
	} else {
		for (i = 0; i < cmd->nargs; i++) {

			kl_get_value(cmd->args[i], &mode, 0, &value);
			if (KL_ERROR) {
				fprintf(KL_ERRORFP, "Could not generate a "
					"trace for task %s\n", cmd->args[i]);
				continue;
			}
			if (mode == 0) {
				fprintf(KL_ERRORFP, "Could not generate a "
					"trace for task %s\n", cmd->args[i]);
				continue;
			}
			if (mode == 1) {
				/* XXX get addr from PID 
				 */
				continue;
			} else {
				addr = (kaddr_t)value;
			}
			if (task_trace(addr, cmd->flags, cmd->ofp)) {
				break;
			}
			trace_cnt++;
		}
	}
	if (trace_cnt) {
		trace_banner(cmd->ofp);
	}
	kl_set_deftask(saved_task);
	return(0);
}

#define _TRACE_USAGE "[-a] [-f] [-w outfile] [[task_list] | [-t tracerec_list]"

/*
 * trace_usage() -- Print the usage string for the 'trace' command.
 */
void
trace_usage(command_t *cmd)
{
	CMD_USAGE(cmd, _TRACE_USAGE);
}

/*
 * trace_help() -- Print the help information for the 'trace' command.
 */
void
trace_help(command_t *cmd)
{
	CMD_HELP(cmd, _TRACE_USAGE,
		"Displays a stack trace for each task included in task_list. "
		"If task_list is empty and deftask is set, then a stack trace "
		"for the default task is displayed. If deftask is not set, "
		"then a trace will be displayed for the task running at the "
		"time of a system PANIC. If the command is issued with the "
		"-t command line option, additional items on the command line "
		"will be treated as pointers to lcrash stack trace records "
		"(prevously allocated using the mktrace command).");
}

/*
 * trace_parse() -- Parse the command line arguments for 'trace'.
 */
int
trace_parse(command_t *cmd)
{
	option_t *op;

	if (set_cmd_flags(cmd, (C_WRITE|C_FULL|C_ALL), "t")) {
		return(1);
	}
	op = cmd->options;
	while (op) {
		switch(op->op_char) {
			case 't':
				cmd->flags |= C_TFLAG;
				break;
		}
		op = op->op_next;
	}
	return(0);
}

/*
 * trace_complete() -- Complete arguments of 'trace' command.
 */
char *
trace_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");
	trace_usage(cmd);
	return(DRAW_NEW_ENTIRE_LINE);
}
