/*	$OpenBSD: db_trace.c,v 1.16 2014/01/30 20:11:14 miod Exp $	*/

/*
 * Copyright (c) 1997 Niklas Hallqvist.  All rights reserved.
 * Copyright (c) 1997 Theo de Raadt.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>

#include <uvm/uvm_extern.h>

#include <machine/db_machdep.h>
#include <machine/frame.h>

#include <ddb/db_access.h>
#include <ddb/db_command.h>
#include <ddb/db_output.h>
#include <ddb/db_sym.h>
#include <ddb/db_variables.h>
#include <ddb/db_extern.h>
#include <ddb/db_interface.h>

extern int	etext;

struct opcode opcode[] = {
	{ OPC_PAL, "call_pal", 0 },	/* 00 */
	{ OPC_RES, "opc01", 0 },	/* 01 */
	{ OPC_RES, "opc02", 0 },	/* 02 */
	{ OPC_RES, "opc03", 0 },	/* 03 */
	{ OPC_RES, "opc04", 0 },	/* 04 */
	{ OPC_RES, "opc05", 0 },	/* 05 */
	{ OPC_RES, "opc06", 0 },	/* 06 */
	{ OPC_RES, "opc07", 0 },	/* 07 */
	{ OPC_MEM, "lda", 1 },		/* 08 */
	{ OPC_MEM, "ldah", 1 },		/* 09 */
	{ OPC_RES, "opc0a", 0 },	/* 0A */
	{ OPC_MEM, "ldq_u", 1 },	/* 0B */
	{ OPC_RES, "opc0c", 0 },	/* 0C */
	{ OPC_RES, "opc0d", 0 },	/* 0D */
	{ OPC_RES, "opc0e", 0 },	/* 0E */
	{ OPC_MEM, "stq_u", 1 },	/* 0F */
	{ OPC_OP, "inta", 0 },		/* 10 */
	{ OPC_OP, "intl", 0 },		/* 11 */
	{ OPC_OP, "ints", 0 },		/* 12 */
	{ OPC_OP, "intm", 0 },		/* 13 */
	{ OPC_RES, "opc14", 0 },	/* 14 */
	{ OPC_OP, "fltv", 1 },		/* 15 */
	{ OPC_OP, "flti", 1 },		/* 16 */
	{ OPC_OP, "fltl", 1 },		/* 17 */
	{ OPC_MEM, "misc", 0 },		/* 18 */
	{ OPC_PAL, "pal19", 0 },	/* 19 */
	{ OPC_MEM, "jsr", 0 },		/* 1A */
	{ OPC_PAL, "pal1b", 0 },	/* 1B */
	{ OPC_RES, "opc1c", 0 },	/* 1C */
	{ OPC_PAL, "pal1d", 0 },	/* 1D */
	{ OPC_PAL, "pal1e", 0 },	/* 1E */
	{ OPC_PAL, "pal1f", 0 },	/* 1F */
	{ OPC_MEM, "ldf", 1 },		/* 20 */
	{ OPC_MEM, "ldg", 1 },		/* 21 */
	{ OPC_MEM, "lds", 1 },		/* 22 */
	{ OPC_MEM, "ldt", 1 },		/* 23 */
	{ OPC_MEM, "stf", 1 },		/* 24 */
	{ OPC_MEM, "stg", 1 },		/* 25 */
	{ OPC_MEM, "sts", 1 },		/* 26 */
	{ OPC_MEM, "stt", 1 },		/* 27 */
	{ OPC_MEM, "ldl", 1 },		/* 28 */
	{ OPC_MEM, "ldq", 1 },		/* 29 */
	{ OPC_MEM, "ldl_l", 1 },	/* 2A */
	{ OPC_MEM, "ldq_l", 1 },	/* 2B */
	{ OPC_MEM, "stl", 1 },		/* 2C */
	{ OPC_MEM, "stq", 1 },		/* 2D */
	{ OPC_MEM, "stl_c", 1 },	/* 2E */
	{ OPC_MEM, "stq_c", 1 },	/* 2F */
	{ OPC_BR, "br", 1 },		/* 30 */
	{ OPC_BR, "fbeq", 1 },		/* 31 */
	{ OPC_BR, "fblt", 1 },		/* 32 */
	{ OPC_BR, "fble", 1 },		/* 33 */
	{ OPC_BR, "bsr", 1 },		/* 34 */
	{ OPC_BR, "fbne", 1 },		/* 35 */
	{ OPC_BR, "fbge", 1 },		/* 36 */
	{ OPC_BR, "fbgt", 1 },		/* 37 */
	{ OPC_BR, "blbc", 1 },		/* 38 */
	{ OPC_BR, "beq", 1 },		/* 39 */
	{ OPC_BR, "blt", 1 },		/* 3A */
	{ OPC_BR, "ble", 1 },		/* 3B */
	{ OPC_BR, "blbs", 1 },		/* 3C */
	{ OPC_BR, "bne", 1 },		/* 3D */
	{ OPC_BR, "bge", 1 },		/* 3E */
	{ OPC_BR, "bgt", 1 },		/* 3F */
};

static __inline int sext(u_int);
static __inline int rega(u_int);
static __inline int regb(u_int);
static __inline int regc(u_int);
static __inline int disp(u_int);

static __inline int
sext(x)
	u_int x;
{
	return ((x & 0x8000) ? -(-x & 0xffff) : (x & 0xffff));
}

static __inline int
rega(x)
	u_int x;
{
	return ((x >> 21) & 0x1f);
}

static __inline int
regb(x)
	u_int x;
{
	return ((x >> 16) & 0x1f);
}

static __inline int
regc(x)
	u_int x;
{
	return (x & 0x1f);
}

static __inline int
disp(x)
	u_int x;
{
	return (sext(x & 0xffff));
}

/*
 * XXX There are a couple of problems with this code:
 *
 *	The argument list printout code is likely to get confused.
 *
 *	It relies on the conventions of gcc code generation.
 *
 *	It uses heuristics to calculate the framesize, and might get it wrong.
 *
 *	It doesn't yet use the framepointer if available.
 *
 *	The address argument can only be used for pointing at trapframes
 *	since a frame pointer of its own serves no good on the alpha,
 *	you need a pc value too.
 *
 *	The heuristics used for tracing through a trap relies on having
 *	symbols available.
 */
void
db_stack_trace_print(addr, have_addr, count, modif, pr)
	db_expr_t       addr;
	int             have_addr;
	db_expr_t       count;
	char            *modif;
	int		(*pr)(const char *, ...);
{
	u_long		*frame;
	int		i, framesize;
	db_addr_t	pc, ra;
	u_int		inst;
	char		*name;
	db_expr_t	offset;
	db_regs_t	*regs;
	u_long		*slot[32];

	bzero(slot, sizeof(slot));
	if (count == -1)
		count = 65535;

	if (have_addr) {
		(*pr)("alpha trace requires a trap frame... giving up.\n");
		return;
	}
	regs = DDB_REGS;
trapframe:
	/* remember where various registers are stored */
	for (i = 0; i < 31; i++)
		slot[i] = &regs->tf_regs[0] +
		    ((u_long *)db_regs[i].valuep - &ddb_regs.tf_regs[0]);
	frame = (u_long *)regs->tf_regs[FRAME_SP];
	pc = (db_addr_t)regs->tf_regs[FRAME_PC];
	ra = (db_addr_t)regs->tf_regs[FRAME_RA];

	while (count-- && pc >= (db_addr_t)KERNBASE && pc < (db_addr_t)&etext) {
		db_find_sym_and_offset(pc, &name, &offset);
		if (!name) {
			name = "?";
			/* Limit the search for procedure start */
			offset = 65536;
		}
		(*pr)("%s(", name);

		framesize = 0;
		for (i = sizeof (int); i <= offset; i += sizeof (int)) {
			inst = *(u_int *)(pc - i);
	
			/*
			 * If by chance we don't have any symbols we have to
			 * get out somehow anyway.  Check for the preceding
			 * procedure return in that case.
			 */
			if (name[0] == '?' && inst_return(inst))
				break;

			/*
			 * Disassemble to get the needed info for the frame.
			 */
			if ((inst & 0xffff0000) == 0x23de0000)
				/* lda sp,n(sp) */
				framesize -= disp(inst) / sizeof (u_long);
			else if ((inst & 0xfc1f0000) == 0xb41e0000)
				/* stq X,n(sp) */
				slot[rega(inst)] =
				    frame + disp(inst) / sizeof (u_long);
			else if ((inst & 0xfc000fe0) == 0x44000400 &&
			    rega(inst) == regb(inst)) {
				/* bis X,X,Y (aka mov X,Y) */
				/* zero is hardwired */
				if (rega(inst) != 31)
					slot[rega(inst)] = slot[regc(inst)];
				slot[regc(inst)] = 0;
				/*
				 * XXX In here we might special case a frame
				 * pointer setup, i.e. mov sp, fp.
				 */
			} else if (db_inst_load(inst))
				/* clobbers a register */
				slot[rega(inst)] = 0;
			else if (opcode[inst >> 26].opc_fmt == OPC_OP)
				/* clobbers a register */
				slot[regc(inst)] = 0;
			/*
			 * XXX Recognize more reg clobbering instructions and
			 * set slot[reg] = 0 then too.
			 */
		}

		/*
		 * Try to print the 6 quads that might hold the args.
		 * We print 6 of them even if there are fewer, cause we don't
		 * know the number.  Maybe we could skip the last ones
		 * that never got used.  If we cannot know the value, print
		 * a question mark.
		 */
		for (i = 0; i < 6; i++) {
			if (i > 0)
				(*pr)(", ");
			if (slot[16 + i])
				(*pr)("%lx", *slot[16 + i]);
			else
				(*pr)("?");
		}

#if 0
		/*
		 * XXX This will go eventually when I trust the argument
		 * printout heuristics.
		 *
		 * Print the stack frame contents.
		 */
		(*pr)(") [%p: ", frame);
		if (framesize > 1) {
			for (i = 0; i < framesize - 1; i++)
				(*pr)("%lx, ", frame[i]);
			(*pr)("%lx", frame[i]);
		}
		(*pr)("] at ");
#else
		(*pr)(") at ");
#endif
		db_printsym(pc, DB_STGY_PROC, pr);
		(*pr)("\n");

		/*
		 * If we are looking at a Xent* routine we are in a trap
		 * context.
		 */
		if (strncmp(name, "Xent", sizeof("Xent") - 1) == 0) {
			regs = (db_regs_t *)frame;
			goto trapframe;
		}

		/* Look for the return address if recorded.  */
		if (slot[26])
			ra = *(db_addr_t *)slot[26];
		else
			break;

		/* Advance to the next frame, if any.  */
		frame += framesize;
		if (ra == pc)
			break;
		pc = ra;
	}
}
