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

static unsigned long
read_reg(struct ia64_frame_info *info, int regnum, int *is_nat)
{
        unsigned long *addr, *rnat_addr, rnat, ret_addr;

        addr = ia64_rse_skip_regs(info->bsp, regnum);
        if (addr < info->regstk.limit || 
		addr >= info->regstk.top || ((long) addr & 0x7) != 0) {
                *is_nat = 1;
                return (0xdeadbeefdeadbeef);
        }
        rnat_addr = ia64_rse_rnat_addr(addr);
        if (rnat_addr >= info->regstk.top) {
                rnat = info->top_rnat;
	}
        else {
		GET_BLOCK((kaddr_t)rnat_addr, 8, (void*)&rnat);
	}
        *is_nat = (rnat & (1UL << ia64_rse_slot_num(addr))) != 0;
	GET_BLOCK((kaddr_t)addr, 8, (void*)&ret_addr);
        return (ret_addr);
}

char INT_NAME[] = "interrupt";

/*
 * find_task_trace()
 */
int
find_task_trace(trace_t *trace, int flags)
{
	int is_nat, curstkidx = 0;
	uaddr_t ksp_offset;
        unsigned long sol, limit, top, cfm;
	struct ia64_frame_info info;
	kaddr_t ip, bsp;
        struct switch_stack *sw;
	char *func_name;
	sframe_t *curframe;
        syment_t *symp = (syment_t *)NULL;


	ksp_offset = (trace->tsp->thread.ksp + 16) - (uaddr_t)trace->task;
        sw = (struct switch_stack *)((uaddr_t)trace->stack[0].ptr + ksp_offset);
        memset(&info, 0, sizeof(info));
        sol = (sw->ar_pfs >> 7) & 0x7f; /* size of locals */
        limit = (uaddr_t)trace->task + IA64_RBS_OFFSET;
        top   = sw->ar_bspstore;
        if (top - (uaddr_t)trace->task >= IA64_STK_OFFSET) {
                top = limit;
	}

        info.regstk.limit = (unsigned long *)limit;
        info.regstk.top   = (unsigned long *)top;
        info.bsp          = ia64_rse_skip_regs(info.regstk.top, -sol);
        info.top_rnat     = sw->ar_rnat;
        info.cfm          = sw->ar_pfs;
        info.ip           = sw->b0;

	while (1) {
		curframe = alloc_sframe(trace, flags);
		ip = ia64_unwind_get_ip(&info);
		bsp = ia64_unwind_get_bsp(&info);
		cfm = info.cfm;
		sol = (cfm >> 7) & 0x7f;        /* size of locals */

        	if (ip) {
			symp = kl_lkup_symaddr(ip);
		}
		if (symp) {
			func_name = symp->s_name;
		} else {
			func_name = INT_NAME;
		}

		UPDATE_FRAME(func_name, ip, 0, cfm, bsp, 0, 0, 0, sol);

		/* In general, we would have to make use of unwind info to
		 * unwind an IA-64 stack, but for now gcc uses a special
		 * convention that makes this possible without full-fledged
		 * unwindo info.  Specifically, we expect "rp" in the second
		 * last, and "ar.pfs" in the last local register, so the
		 * number of locals in a frame must be at least two.  If it's
		 * less than that, we reached the end of the C call stack.
		 */
		if (sol < 2) {
			break;
		}

		info.ip = read_reg(&info, sol - 2, &is_nat);
		if (is_nat) {
			break;
		}

		cfm = read_reg(&info, sol - 1, &is_nat);
		if (is_nat) {
			break;
		}
		sol = (cfm >> 7) & 0x7f;
		info.cfm = cfm;
		info.bsp = ia64_rse_skip_regs(info.bsp, -sol);
	}
	return(0);
}

#ifdef NOT
/*
 * get_call_pc()
 */
kaddr_t
get_call_pc(kaddr_t ra)
{
	kaddr_t addr = 0;
	instr_rec_t *irp;

	if (!(irp = get_instr_stream(ra, 1, 0))) {
		return((kaddr_t)NULL);
	}
	if (!irp->prev) {
		free_instr_stream(irp);
		return((kaddr_t)NULL);
	}
	if ((irp->prev->opcode == 0x00e8) || (irp->prev->opcode == 0xff02)) {
		addr = irp->prev->addr;
	}
	free_instr_stream(irp);
	return(addr);
}

/*
 * get_jmp_instr()
 */
int
get_jmp_instr(
kaddr_t addr, 
kaddr_t isp, 
kaddr_t *caddr, 
char *fname, 
char **cfname)
{
	int ret = 0;
	kaddr_t a;
	unsigned char opcode, modr, sib; 
	syment_t *sp;
	int offset;
	instr_rec_t *irp;

	if (!(irp = get_instr_stream(addr, 1, 0))) {
		return(1);
	}
	if (!irp->prev) {
		free_instr_stream(irp);
		return(1);
	}
	irp = irp->prev;
	if (!(irp->opcode == 0x00e8) && !(irp->opcode == 0xff02)) {
		free_instr_stream(irp);
		return(1);
	}

	/* Check for the easiest case first...
	 */
	if (irp->opcode == 0xe8) {
		a = irp->operand[0].op_addr;
		if (*cfname = kl_funcname(a)) {
			*caddr = a;
		}
	} else if (irp->opcode == 0xff02) {
		switch (irp->modrm) {
			case 0x14:
				if (irp->sib == 0x85) {
					kl_get_kaddr(addr - 4, &a);
					if (KL_ERROR) {
						free_instr_stream(irp);
						return(1);
					}
					if (strstr(fname, "system_call")) {
						GET_BLOCK(isp + 28, 4, &offset);
						a += (offset * 4);
						kl_get_kaddr(a, &a);
						if (*cfname = kl_funcname(a)) {
							*caddr = a;
						}
					}
				}
				break;

			case 0xc2: /* EAX */
			case 0xca: /* ECX */
			case 0xd2: /* EDX */
			case 0xda: /* EBX */
			case 0xea: /* EBP */
			case 0xf2: /* ESI */
			case 0xfa: /* EDI */
				break;
		} 
	}
	free_instr_stream(irp);
	return(0);
}

/* 
 * is_push()
 */
int
is_push(unsigned int opcode)
{
	switch(opcode) {
		case 0x0006:
		case 0x000e:
		case 0x0016:
		case 0x001e:
		case 0x0050:
		case 0x0051:
		case 0x0052:
		case 0x0053:
		case 0x0054:
		case 0x0055:
		case 0x0056:
		case 0x0057:
		case 0x0068:
		case 0x006a:
		case 0x009c:
		case 0x0fa0:
		case 0x0fa8:
		case 0xff06:
			return(1);
		case 0x0060:
			return(2);
	}
	return(0);
}

/* 
 * is_pop()
 */
int
is_pop(unsigned int opcode)
{
	switch(opcode) {
		case 0x0007:
		case 0x0017:
		case 0x001f:
		case 0x0058:
		case 0x0059:
		case 0x005a:
		case 0x005b:
		case 0x005c:
		case 0x005d:
		case 0x005e:
		case 0x005f:
		case 0x008f:
		case 0x009d:
		case 0x0fa1:
		case 0x0fa9:
			return(1);
		case 0x0061:
			return(2);
	}
	return(0);
}

/*
#define FRMSIZE_DBG 1
#define FRMSIZE2_DBG 1
*/

/*
 * get_framesize()
 */
int
get_framesize(kaddr_t pc)
{
	int size, ret, frmsize = 0;
	kaddr_t addr;
	instr_rec_t irp;
        syment_t *sp;

	if (!(sp = kl_lkup_symaddr(pc))) {
		return(-1);
	}
#ifdef FRMSIZE_DBG
	fprintf(stderr, "get_framesize(): pc=0x%x (0x%x:%s)\n", 
		pc, sp->s_addr, sp->s_name);
#endif
	addr = sp->s_addr;
	while (addr <= pc) {
		bzero(&irp, sizeof(irp));
		irp.aflag = 1;
		irp.dflag = 1;
		if (!(size = get_instr_info(addr, &irp))) {
			fprintf(stderr, "ZERO SIZE!!\n");
			return(-1);
		}
		if (size != irp.size) {
			fprintf(stderr, "SIZE DOES NOT MATCH!!\n");
		}
		if (irp.opcode == 0x8300) {
			/* e.g., addl   $0x8,%esp */ 
			if (irp.operand[0].op_reg == R_eSP) {
				frmsize -= irp.operand[1].op_addr;
#ifdef FRMSIZE_DBG
				fprintf(stderr, "    addl  --> 0x%x: -%d\n", 
					addr, irp.operand[1].op_addr);
#endif
			}
		} else if ((irp.opcode == 0x8305) || (irp.opcode == 0x8105)) {
			/* e.g., subl   $0x40,%esp */
			if (irp.operand[0].op_reg == R_eSP) {
				frmsize += irp.operand[1].op_addr;
#ifdef FRMSIZE_DBG
				fprintf(stderr, "    subl  --> 0x%x: +%d\n", 
					addr, irp.operand[1].op_addr);
#endif
			}
		} else if (ret = is_push(irp.opcode)) {
			if (ret == 2) {
				frmsize += (8 * 4);
#ifdef FRMSIZE_DBG
				fprintf(stderr, "   pusha  --> 0x%x: +%d\n",
					addr, (8 * 4));
#endif
			} else {
				frmsize += 4; 
#ifdef FRMSIZE_DBG
				fprintf(stderr, "   pushl  --> 0x%x: +%d\n" ,
					addr, 4);
#endif
			}
		} else if (ret = is_pop(irp.opcode)) {
			if (ret == 2) {
				frmsize -= (8 * 4);
#ifdef FRMSIZE_DBG
				fprintf(stderr, "    popa  --> 0x%x: -%d\n", 
					addr, (8 * 4));
#endif
			} else {
				frmsize -= 4;
#ifdef FRMSIZE_DBG
				fprintf(stderr, "    popl  --> 0x%x: -%d\n", 
					addr, 4);
#endif
			}
#ifdef FRMSIZE2_DBG
		} else {
			fprintf(stderr, "              0x%x: opcode=0x%x\n", 
				addr, irp.opcode);
#endif
		}
		addr += size;
	}
	return(frmsize);
}

/*
 * print_pc()
 */
void
print_pc(kaddr_t addr, FILE *ofp)
{
	int offset = 0;
	syment_t *sp;

	if (sp = kl_lkup_symaddr(addr)) {
		offset = addr - sp->s_addr;
	}

	/* Print out address
	 */
	fprintf(ofp, "0x%x", addr);

	/* Print out symbol name
	 */
	if (sp) {
		if (offset) {
			fprintf(ofp, " <%s+%d>", sp->s_name, offset);
		} else {
			fprintf(ofp, " <%s>", sp->s_name);
		}
	}
}
#endif

/*
 * alloc_sframe() -- Allocate a stack frame record
 */
sframe_t *
alloc_sframe(trace_t *trace, int flags)
{
        sframe_t *f;

	if (flags & C_PERM) {
        	f = (sframe_t *)kl_alloc_block(sizeof(sframe_t), K_PERM);
	} else {
        	f = (sframe_t *)kl_alloc_block(sizeof(sframe_t), K_TEMP);
	}
        if (!f) {
                return((sframe_t *)NULL);
        }
        f->level = trace->nframes;
        return(f);
}

/*
 * free_sframes() -- Free all stack frames allocated to a trace record.
 */
void
free_sframes(trace_t *t)
{
        sframe_t *sf;

        t->nframes = 0;
        sf = t->frame;
        while(t->frame) {
                sf = (sframe_t *)kl_dequeue((element_t **)&t->frame);
                if (sf->srcfile) {
                        kl_free_block((void *)sf->srcfile);
                }
                kl_free_block((void *)sf);
        }
	t->frame = (sframe_t *)NULL;
}

/*
 * alloc_trace_rec() -- Allocate stack trace header
 */
trace_t *
alloc_trace_rec(int flags)
{
        trace_t *t;

	if (flags & C_PERM) {
		t = (trace_t *)kl_alloc_block(sizeof(trace_t), K_PERM);
	} else {
		t = (trace_t *)kl_alloc_block(sizeof(trace_t), K_TEMP);
	}
        return(t);
}

/*
 * free_trace_rec() -- Free memory associated with stack trace header
 */
void
free_trace_rec(trace_t *t)
{
        int i;

        if (t->tsp) {
                kl_free_block(t->tsp);
        }
        for (i = 0; i < STACK_SEGMENTS; i++) {
                if (t->stack[i].ptr) {
                        kl_free_block((void *)t->stack[i].ptr);
                }
        }
        free_sframes(t);
        kl_free_block((void *)t);
}

/*
 * clean_trace_rec() -- Clean up stack trace record without releasing
 *                      any of the allocated memory (except sframes).
 */
void
clean_trace_rec(trace_t *t)
{
	int i;

	t->flags = 0;
	t->task = 0;
	if (t->tsp) {
		kl_free_block(t->tsp);
		t->tsp = 0;
	}
	t->stackcnt = 0;
	for (i = 0; i < STACK_SEGMENTS; i++) {
		if (t->stack[i].ptr) {
			t->stack[i].type = 0;
			t->stack[i].size = 0;
			t->stack[i].addr = (kaddr_t)NULL;
			kl_free_block((void *)t->stack[i].ptr);
			t->stack[i].ptr = (uaddr_t *)NULL;
		}
	}
	free_sframes(t);
}

/* 
 * setup_trace_rec()
 */
int
setup_trace_rec(kaddr_t saddr, kaddr_t task, int flag, trace_t *trace)
{
	int aflag = K_TEMP;
	kl_reset_error();

	if (flag & C_PERM) {
		aflag = K_PERM;
	}
	if (task) {
		trace->task = task;
		trace->tsp = (struct task_struct *)
			kl_alloc_block(sizeof(*trace->tsp), aflag);
		if (kl_get_task_struct(task, 2, trace->tsp)) {
			kl_free_block(trace->tsp);
			trace->tsp = (struct task_struct *)NULL;
			return(1);
		}
	}
	trace->stack[0].type = S_KERNELSTACK;
	trace->stack[0].size = STACK_SIZE;

	/* Get the base address of the stack
	 */
	trace->stack[0].addr = saddr - trace->stack[0].size;
	trace->stack[0].ptr = kl_alloc_block(STACK_SIZE, aflag);
	if (KL_ERROR) {
		clean_trace_rec(trace);
		return(1);
	}
	GET_BLOCK(trace->stack[0].addr, STACK_SIZE, trace->stack[0].ptr);
	if (KL_ERROR) {
		clean_trace_rec(trace);
		return(1);
	}
	return(0);
}

#ifdef NOT
/*
 * valid_ra()
 */
int
valid_ra(kaddr_t ra)
{
	kaddr_t pc;

	if ((ra < KL_PAGE_OFFSET) || !kl_funcaddr(ra)) {
		return(0);
	}
	if (pc = get_call_pc(ra)) {
		return(1);
	}
	return(0);
}
#endif

/*
 * find_trace()
 *
 *   Given a starting pc (start_cp), starting stack pointer (start_sp), 
 *   and stack address, check to see if a valid trace is possible. A
 *   trace is considered valid if no errors are encountered (bad PC,
 *   bad SP, etc.) Certain errors are tolorated however. For example,
 *   if the current stack frame is an exception frame (e.g., VEC_*),
 *   go ahead and return success -- even if PC and SP obtained from
 *   the exception frame are bad (a partial trace is better than no
 *   trace)..
 *
 *   Return zero if no valid trace was found. Otherwise, return the
 *   number of frames found. If the C_ALL flag is passed in, then
 *   return a trace even if it is a subtrace of a trace that was
 *   previously found.
 *
 *   Parameters:
 *
 *   start_pc       starting program counter
 *   start_sp       starting stack pointer
 *   check_pc       if non-NULL, check to see if check_pc/check_sp
 *   check_sp       are a sub-trace of trace beginning with spc/ssp
 *   trace          structure containing all trace related info (frames,
 *                  pages, page/frame counts, etc.
 *   flags
 */
int
find_trace(
	kaddr_t start_pc, 
	kaddr_t start_sp, 
	kaddr_t check_pc, 
	kaddr_t check_sp,
	trace_t *trace, 
	int flags)
{
#ifdef NOT
	int size, curstkidx = 0, frame_size;
	kaddr_t sp, pc, ra, bp, sbase, saddr, func_addr;
	sframe_t *curframe, *sf;
	char *func_name;
	uaddr_t *sbp, *asp;	

	sbp = trace->stack[curstkidx].ptr;
	sbase = trace->stack[curstkidx].addr;
	saddr = sbase + trace->stack[curstkidx].size;
	bp = start_sp + get_framesize(start_pc); 
	if (KL_ERROR || (bp < sbase) || (bp >= saddr)) {
		return(0);
	}
	pc = start_pc;
	sp = start_sp;
	func_name = kl_funcname(pc);

	while (pc) {

		/* LOOP TRAP! Make sure we are not just looping on the
		 * same frame forever.
		 */
		if ((trace->nframes > 1) &&
			(curframe->funcname == curframe->prev->funcname) &&
				(curframe->sp == curframe->prev->sp)) {
			curframe->error = 1;
			return(trace->nframes);
		} 

		/* Allocate space for a stack frame rec 
		 */
		curframe = alloc_sframe(trace, flags);
		if (!(func_addr = kl_funcaddr(pc))) {
			curframe->error = KLE_BAD_PC;
			UPDATE_FRAME(0, pc, 0, 0, 0, 0, 0, 0, 0);
			return(trace->nframes);
		}

		/* Check to see if check_pc/check_sp points to a sub-trace
		 * of spc/ssp. If it does then don't return a trace (unless 
		 * C_ALL). Make sure we free the curframe block since we 
		 * wont be linking it in to the trace rec.
		 */
		if (check_pc && ((pc == check_pc) && (sp == check_sp))) {
			kl_free_block((void *)curframe);
			if (flags & C_ALL) {
				return(trace->nframes);
			} else {
				return(0);
			}
		}
		asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - (saddr - sp)));
		kl_get_kaddr(bp + 4, &ra);

		/* Make sure that the ra we have is a valid one. If not
		 * then back up in the frame, word by word, until we find 
		 * one that is good.
		 */
		if (!valid_ra(ra)) {
			int i;

			i = ((bp - sp + 8) / 4);
			while (i) {
				bp -= 4;
				kl_get_kaddr(bp + 4, &ra);
				if (valid_ra(ra)) {
					break;
				}
				i--;
			}
			if (i == 0)  {
				curframe->error = KLE_BAD_RA;
				UPDATE_FRAME(func_name, pc, ra, sp, 
					bp + 4, asp, 0, 0, 0);
				return(trace->nframes);
			}
		}
		UPDATE_FRAME(func_name, pc, ra, sp, bp + 4, asp, 0, 0, 0);
		curframe->frame_size = curframe->fp - curframe->sp + 4;

		/* Gather starting information for the next frame
		 */
		pc = get_call_pc(ra);
#ifdef USE_FRAMEPTRS
		kl_get_kaddr(bp, &bp);
		if (KL_ERROR) {
			curframe->error = 2;
			return(trace->nframes);
		}
#else 
		/* It's possible for get_framesize() to return a size
		 * that is larger than the actual frame size (because
		 * all it does is count the push, pop, addl, and subl
		 * instructions that effect the SP). If we are real near
		 * the top of the stack, this might cause bp to overflow.
		 * This will be fixed above, but we need to bring bp 
		 * back into the legal range so we don't crap out
		 * before we can get to it...
		 */
		frame_size = get_framesize(pc);
		if ((curframe->fp + frame_size) >= saddr) {
			bp = saddr - 4;
		} else {
			bp = curframe->fp + frame_size;
		}
#endif
		if (func_name = kl_funcname(pc)) {
			if (strstr(func_name, "kernel_thread")) {
				ra = 0;
				bp = saddr - 4;
				asp = (uaddr_t*)
					((uaddr_t)sbp + (STACK_SIZE - 12));
				curframe = alloc_sframe(trace, flags);
				UPDATE_FRAME(func_name, pc, 
					ra, sp, bp, asp, 0, 0, 16);
				return(trace->nframes);
			} else if (strstr(func_name, "is386")) {
				ra = 0;
				bp = sp = saddr - 4;
				asp = curframe->asp;
				curframe = alloc_sframe(trace, flags);
				UPDATE_FRAME(func_name, pc, 
					ra, sp, bp, asp, 0, 0, 0);
				return(trace->nframes);
			} else if (strstr(func_name, "system_call")) {
				bp = saddr - 20;
				sp = curframe->fp + 4;
				kl_get_kaddr(bp, &ra);	
				curframe = alloc_sframe(trace, flags);
				asp = (uaddr_t*)((uaddr_t)sbp + 
					(STACK_SIZE - (saddr - sp)));
				UPDATE_FRAME(func_name, pc, 
					ra, sp, bp, asp, 0, 0, (bp - sp + 4));
				return(trace->nframes);
			}
		}

		/* Make sure our next frame pointer is valid (in the stack).
		 */
		if ((bp < sbase) || (bp >= saddr)) {
			curframe->error = 3;
			return(trace->nframes);
		}
		sp = curframe->fp + 4;
	}
#endif
	return(trace->nframes);
}

/*
 * pc_offset()
 */
int
pc_offset(kaddr_t pc) 
{
	kaddr_t func_addr;

	if (func_addr = kl_funcaddr(pc)) {
		return(pc - func_addr);
	}
	return(-1);
}

/*
 * dump_stack_frame()
 */
void
dump_stack_frame(trace_t *trace, sframe_t *curframe, FILE *ofp)
{
	int i, first_time = 1;
	kaddr_t sp;
	uaddr_t *asp;

	sp = curframe->sp;
	asp = curframe->asp;

	for (i = 0; i < curframe->frame_size / 4; i++) {
		if (!(i % 4)) {
			if (first_time) {
				first_time = 0;
				fprintf(ofp, "   %x: %08x  ", sp, *asp++);
			} else {
				fprintf(ofp, "\n   %x: ", sp);
				fprintf(ofp, "%08x  ", *asp++);
			}
			sp += 16;
		} else  {
			fprintf(ofp, "%08x  ", *asp++);
		}
	}
	if (curframe->frame_size) {
		fprintf(ofp, "\n\n");
	}
}

/*
 * print_trace()
 */
int
print_trace(trace_t *trace, int flags, FILE *ofp)
{
	int offset;
	sframe_t *frmp;

	if (frmp = trace->frame) {
		do {
			fprintf(ofp, "%2d %s", frmp->level, frmp->funcname);
			offset = pc_offset(frmp->pc);
			if (offset > 0) {
				fprintf(ofp, "+%d", offset);
			} else if (offset < 0) {
				fprintf(ofp, "+<ERROR>");
			}
			fprintf(ofp, " [0x%llx]\n", frmp->pc);
			if (flags & C_FULL) {
				fprintf(ofp, "\n");
				fprintf(ofp, "   RA=0x%llx, SP=0x%llx, "
					"FP=0x%llx, SIZE=%d\n\n", 
					frmp->ra, frmp->sp, 
					frmp->fp, frmp->frame_size);
#ifdef FRMSIZE_DBG
				fprintf(ofp, "\n  FRAMESIZE=%d\n\n",
					get_framesize(frmp->pc));
#endif
#ifdef NOT
				dump_stack_frame(trace, frmp, ofp);
#endif
			}
			if (frmp->error) {
				fprintf(ofp, "TRACE ERROR 0x%llx\n", 
					frmp->error);
			}
			frmp = frmp->next;
		} while (frmp != trace->frame);
	}
}

/* 
 * trace_banner()
 */
void
trace_banner(FILE *ofp)
{
	fprintf(ofp, "===================================================="
			"============\n");
}

/*
 * task_trace()
 */
int
task_trace(kaddr_t task, int flags, FILE *ofp)
{
	struct task_struct *tsp;
	kaddr_t saddr, eip, esp;
	trace_t *trace;

	if (!(trace = (trace_t *)alloc_trace_rec(C_TEMP))) {
		fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
		return(1);
	} else {
		saddr = kl_kernelstack(task);
		setup_trace_rec(saddr, task, 0, trace);
		if (KL_ERROR) {
			fprintf(KL_ERRORFP, "Error setting up trace rec!\n");
			free_trace_rec(trace);
			return(1);
		}
		find_task_trace(trace, 0);
		trace_banner(ofp);
		fprintf(ofp, "STACK TRACE FOR TASK: ");
		print_kaddr(task, ofp, 0);
		fprintf(ofp, " (%s)\n\n", trace->tsp->comm);
		print_trace(trace, flags, ofp);
	}
	free_trace_rec(trace);
	return(0);
}

/*
 * print_traces()
 *
 *   Output a list of all valid code addresses contained in a stack
 *   along with their function name and stack location.
 */
int
print_traces(kaddr_t saddr, int level, int flags, FILE *ofp)
{
#ifdef NOT
	int nfrms;
	unsigned char instr; 
	int offset;
	char *fname, *cfname;
	uaddr_t *wordp, *stackp;
	trace_t *trace;
	kaddr_t addr, isp, caddr, sbase;
	
	stackp = (uaddr_t*)kl_alloc_block(STACK_SIZE, K_TEMP);
	sbase = saddr - STACK_SIZE;
	GET_BLOCK(sbase, STACK_SIZE, stackp);
	if (KL_ERROR) {
		kl_free_block(stackp);
		return(1);
	}

	if (!(trace = (trace_t *)alloc_trace_rec(K_TEMP))) {
		fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
		kl_free_block(stackp);
		return(1);
	}
	setup_trace_rec(saddr, 0, 0, trace);

	wordp = stackp;
	while(wordp < (stackp + (STACK_SIZE / 4))) {
		if (addr =  (kaddr_t)(*(uaddr_t*)wordp)) {

			/* check to see if this is a valid code address
			 */
			if (fname = kl_funcname(addr)) {
				/* Now use the instruction to back up and
				 * see if this RA was saved after a call.
				 * If it was, then try to determine what 
				 * function was called. At the very least,
				 * only print out info for true return
				 * addresses (coming right after a call
				 * instruction -- even if we can't tell
				 * what function was called).
				 */
				isp = sbase + (((uaddr_t)wordp) - 
						((uaddr_t)stackp));

				cfname = (char *)NULL;
				caddr = 0;
				if (get_jmp_instr(addr, isp, 
						&caddr, fname, &cfname)) {
					wordp++;
					continue;
				}

				/* We have found a valid jump address. Now, 
				 * try and get a backtrace.
				 */
				nfrms = find_trace(addr, isp, 0, 0, trace, 0);
				if (nfrms) {
					if ((nfrms >= level) &&
						 (!trace->frame->prev->error ||
							(flags & C_ALL))) {
						fprintf(ofp, "\nPC=");
						print_kaddr(addr, ofp, 0);
						fprintf(ofp, "  SP=");
						print_kaddr(isp, ofp, 0);
						fprintf(ofp, "  SADDR=");
						print_kaddr(saddr, ofp, 0);
						fprintf(ofp, "\n");
						trace_banner(ofp);
						print_trace(trace, flags, ofp);
						trace_banner(ofp);
					}
					free_sframes(trace);
				}
			}
			wordp++;
		} else {
			wordp++;
		}
	}
	kl_free_block(stackp);
#endif
	return(0);
}

/*
 * do_list()
 *
 *   Output a list of all valid code addresses contained in a stack
 *   along with their function name and stack location.
 */
int
do_list(kaddr_t saddr, int size, FILE *ofp)
{
#ifdef NOT
	unsigned char instr; 
	int offset;
	char *fname, *cfname;
	uaddr_t *wordp, *stackp;
	kaddr_t addr, isp, caddr, sbase;
	
	stackp = (uaddr_t*)kl_alloc_block(size, K_TEMP);
	sbase = saddr - size;
	GET_BLOCK(sbase, size, stackp);
	if (KL_ERROR) {
		kl_free_block(stackp);
		return(1);
	}

	wordp = stackp;
	while(wordp < (stackp + (size / 4))) {
		if (addr =  (kaddr_t)(*(uaddr_t*)wordp)) {

			/* check to see if this is a valid code address
			 */
			if (fname = kl_funcname(addr)) {
				/* Now use the instruction to back up and
				 * see if this RA was saved after a call.
				 * If it was, then try to determine what 
				 * function was called. At the very least,
				 * only print out info for true return
				 * addresses (coming right after a call
				 * instruction -- even if we can't tell
				 * what function was called).
				 */
				isp = sbase + (((uaddr_t)wordp) - 
						((uaddr_t)stackp));

				cfname = (char *)NULL;
				caddr = 0;
				if (get_jmp_instr(addr, isp, 
						&caddr, fname, &cfname)) {
					wordp++;
					continue;
				}
				fprintf(ofp, "0x%x -- 0x%x (%s)",
						isp, addr, fname);
				if (cfname) {
					fprintf(ofp, " --> 0x%x (%s)\n",
						caddr, cfname);
				} else {
					fprintf(ofp, "\n");
				}
			}
			wordp++;
		} else {
			wordp++;
		}
	}
	kl_free_block(stackp);
#endif
	return(0);
}

/*
 * add_frame()
 */
int
add_frame(trace_t *trace, kaddr_t fp, kaddr_t ra)
{
	sframe_t *cf, *sf;

	/* Check to make sure that sp is from the stack in the trace
	 * record.
	 *
	 * XXX -- todo
	 */
	sf = (sframe_t *)alloc_sframe(trace, C_PERM);
	sf->fp = fp;
	sf->ra = ra;
	if (cf = trace->frame) {
		do {
			if (cf->fp && (sf->fp < cf->fp)) {
				if (cf->next == cf) {
					cf->prev = sf;
					sf->next = cf;
					cf->next = sf;
					sf->prev = cf;
					trace->frame = sf;
				} else {
					cf->prev->next = sf;
					sf->prev = cf->prev;
					cf->prev = sf;
					sf->next = cf;
				}
				return(0);
			}
			cf = cf->next;
		} while (cf != trace->frame);
		cf = 0;
	} 
	if (!cf) {
		kl_enqueue((element_t **)&trace->frame, (element_t *)sf);
	}
}

/*
 * finish_trace()
 */
finish_trace(trace_t *trace)
{
#ifdef NOT
	int level = 0, curstkidx = 0;
	uaddr_t *sbp, *asp;
	kaddr_t sbase, saddr;
	sframe_t *sf;

	sbp = trace->stack[curstkidx].ptr;
        sbase = trace->stack[curstkidx].addr;
        saddr = sbase + trace->stack[curstkidx].size;

	if (sf = trace->frame) {
		do {
			if (!sf->pc) {
				if (sf != trace->frame) {
					sf->sp = sf->prev->fp + 4;
					sf->pc = get_call_pc(sf->prev->ra);
				}
				if (!sf->pc) {
					sf = sf->next;
					continue;
				}
			}
			sf->level = level++;
			sf->frame_size = sf->fp - sf->sp + 4;
			sf->funcname = kl_funcname(sf->pc);
			sf->asp = (uaddr_t*)((uaddr_t)sbp + 
				(STACK_SIZE - (saddr - sf->sp)));
			sf = sf->next;
		} while (sf != trace->frame);

		if (level > 0) {
			sf = (sframe_t *)alloc_sframe(trace, C_PERM);
			sf->level = level;
			sf->sp = trace->frame->prev->fp + 4;
			sf->pc = get_call_pc(trace->frame->prev->ra);
			sf->funcname = kl_funcname(sf->pc);
			if (sf->funcname && 
					strstr(sf->funcname, "kernel_thread")) {
				sf->ra = 0;
				sf->fp = saddr - 4;
				sf->asp = (uaddr_t*)((uaddr_t)sbp + 
					(STACK_SIZE - 12));
			} else {
				sf->fp = saddr - 20;
				kl_get_kaddr(sf->fp, &sf->ra);
				sf->asp = (uaddr_t*)((uaddr_t)sbp + 
					(STACK_SIZE - (saddr - sf->sp)));
			}
			sf->frame_size = sf->fp - sf->sp + 4;
			kl_enqueue((element_t **)&trace->frame, 
				(element_t *)sf);
		}
	}
#endif
}
