[BACK]Return to db_trace.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / arch / pc532 / pc532

File: [cvs.NetBSD.org] / src / sys / arch / pc532 / pc532 / Attic / db_trace.c (download)

Revision 1.9, Fri May 26 03:34:28 2000 UTC (20 years, 9 months ago) by jhawk
Branch: MAIN
CVS Tags: netbsd-1-5-base, netbsd-1-5-RELEASE, netbsd-1-5-PATCH002, netbsd-1-5-PATCH001, netbsd-1-5-BETA2, netbsd-1-5-BETA, netbsd-1-5-ALPHA2, minoura-xpg4dl-base, minoura-xpg4dl
Branch point for: netbsd-1-5
Changes since 1.8: +27 -26 lines

Rename the machine-specific stack trace printing functions
from db_stack_trace_cmd() to db_stack_trace_print(),
and add an additional argument, a function pointer for an
output routine (i.e. printf() or db_printf()).

Add db_stack_trace_cmd() in db_command.[ch], calling
db_stack_trace_print() with db_printf() as the printer.

Move count==-1 special handling from db_stack_trace_print() [nee
db_stack_trace_cmd()] to db_stack_trace_cmd() [nascent here].

Again, I'm unable to test compilation on all affected platforms,
so advance apologies for potential brokenness.

/*	$NetBSD: db_trace.c,v 1.9 2000/05/26 03:34:28 jhawk Exp $	*/

/* 
 * Mach Operating System
 * Copyright (c) 1992 Carnegie Mellon University
 * Copyright (c) 1992 Helsinki University of Technology
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON AND HELSINKI UNIVERSITY OF TECHNOLOGY ALLOW FREE USE
 * OF THIS SOFTWARE IN ITS "AS IS" CONDITION.  CARNEGIE MELLON AND
 * HELSINKI UNIVERSITY OF TECHNOLOGY DISCLAIM ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon 
 * the rights to redistribute these changes.
 */
/*
 * 	File: ns532/db_trace.c
 *	Author: Tero Kivinen, Tatu Ylonen
 *	Helsinki University of Technology 1992.
 *
 *	Stack trace and special register support for debugger.
 */


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

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

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

struct ns532_frame;
static void	db_nextframe __P((struct ns532_frame **, db_addr_t *,
    int *, int, void (*)(const char *, ...)));
static int	db_numargs __P((struct ns532_frame *));
static int	db_spec_regs __P((struct db_variable *, db_expr_t *, int,
    void (*)(const char *, ...)));

/*
 * Machine register set.
 */
struct db_variable db_regs[] = {
	{ "r0", 	(long *)&ddb_regs.tf_regs.r_r0, FCN_NULL },
	{ "r1", 	(long *)&ddb_regs.tf_regs.r_r1, FCN_NULL },
	{ "r2", 	(long *)&ddb_regs.tf_regs.r_r2, FCN_NULL },
	{ "r3", 	(long *)&ddb_regs.tf_regs.r_r3, FCN_NULL },
	{ "r4", 	(long *)&ddb_regs.tf_regs.r_r4, FCN_NULL },
	{ "r5", 	(long *)&ddb_regs.tf_regs.r_r5, FCN_NULL },
	{ "r6", 	(long *)&ddb_regs.tf_regs.r_r6, FCN_NULL },
	{ "r7", 	(long *)&ddb_regs.tf_regs.r_r7, FCN_NULL },
	{ "sp", 	(long *)&ddb_regs.tf_regs.r_sp, FCN_NULL },
	{ "fp", 	(long *)&ddb_regs.tf_regs.r_fp, FCN_NULL },
	{ "sb", 	(long *)&ddb_regs.tf_regs.r_sb, FCN_NULL },
	{ "pc", 	(long *)&ddb_regs.tf_regs.r_pc, FCN_NULL },
	{ "psr",	(long *)&ddb_regs.tf_regs.r_psr,FCN_NULL },
	{ "tear",	(long *)&ddb_regs.tf_tear,	FCN_NULL },
	{ "msr",	(long *)&ddb_regs.tf_msr,	FCN_NULL },
	{ "ipl",	(long *)&db_active_ipl, 	FCN_NULL },

	{ "intbase",	(long *) 0, db_spec_regs },
	{ "ptb",	(long *) 0, db_spec_regs },
	{ "ivar",	(long *) 0, db_spec_regs },
	{ "rtear",	(long *) 0, db_spec_regs }, /* Current reg value */
	{ "mcr",	(long *) 0, db_spec_regs },
	{ "rmsr",	(long *) 0, db_spec_regs }, /* Current reg value */
	{ "dcr",	(long *) 0, db_spec_regs },
	{ "dsr",	(long *) 0, db_spec_regs },
	{ "car",	(long *) 0, db_spec_regs },
	{ "bpc",	(long *) 0, db_spec_regs },
	{ "cfg",	(long *) 0, db_spec_regs },
};
struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]) - 1;

/*
 * Stack trace.
 */
#define	INKERNEL(va)	(((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS)

struct ns532_frame {
	struct ns532_frame	*f_frame;
	int			f_retaddr;
	int			f_arg0;
};

enum { NONE, TRAP, INTERRUPT, SYSCALL };

db_addr_t	db_trap_symbol_value = 0;
db_addr_t	db_syscall_symbol_value = 0;
db_addr_t	db_intr_symbol_value = 0;
boolean_t	db_trace_symbols_found = FALSE;

/*
 * Figure out how many arguments were passed into the frame at "fp".
 */
int db_numargs_default = 5;

int
db_numargs(fp)
	struct ns532_frame *fp;
{
	db_addr_t	argp;
	int		inst;
	int		args;
	int		i;
	extern char	etext[];

	argp = (db_addr_t) db_get_value((db_addr_t)&fp->f_retaddr, 4, FALSE);
	if (argp < (db_addr_t)VM_MIN_KERNEL_ADDRESS || argp > (db_addr_t)etext)
		return(db_numargs_default);

	for (i = 0; i < 5; i++) {
		/*
		 * After a bsr gcc may emit the following instructions
		 * to remove the arguments from the stack:
		 *   cmpqd 0,tos 	- to remove 4 bytes from the stack
		 *   cmpd tos,tos	- to remove 8 bytes from the stack
		 *   adjsp[bwd] -n	- to remove n bytes from the stack
		 * Gcc sometimes delays emitting these instructions and
		 * may even throw a branch between our feet.
		 */
		inst = db_get_value((db_addr_t) argp	, 4, FALSE);
		args = db_get_value((db_addr_t) argp + 2, 4, FALSE);
		if ((inst & 0xff) == 0xea) {		/* br */
			args = ((inst >> 8) & 0xffffff) | (args << 24);
			if (args & 0x80) {
				if (args & 0x40) {
					args = ntohl(args);
				} else {
					args = ntohs(args & 0xffff);
					if (args & 0x2000)
						args |= 0xc000;
				}
			} else {
				args = args & 0xff;
				if (args & 0x40)
					args |= 0x80;
			}
			argp += args;
			continue;
		}
		if ((inst & 0xffff) == 0xb81f)		/* cmpqd 0,tos */
			return(1);
		else if ((inst & 0xffff) == 0xbdc7)	/* cmpd tos,tos */
			return(2);
		else if ((inst & 0xfffc) == 0xa57c) {	/* adjsp[bwd] */
			switch (inst & 3) {
			case 0:
				args = ((args & 0xff) + 0x80);
				break;
			case 1:
				args = ((ntohs(args) & 0xffff) + 0x8000);
				break;
			case 3:
				args = -ntohl(args);
				break;
			default:
				return(db_numargs_default);
			}
			if (args / 4 > 10 || (args & 3) != 0)
				continue;
			return(args / 4);
		}
		argp += db_dasm_ns32k(NULL, argp);
	}
	return(db_numargs_default);
}

/*
 * Figure out the next frame up in the call stack.
 * For trap(), we print the address of the faulting instruction and
 *   proceed with the calling frame.  We return the ip that faulted.
 *   If the trap was caused by jumping through a bogus pointer, then
 *   the next line in the backtrace will list some random function as
 *   being called.  It should get the argument list correct, though.
 *   It might be possible to dig out from the next frame up the name
 *   of the function that faulted, but that could get hairy.
 */
static void
db_nextframe(fp, ip, argp, is_trap, pr)
	struct ns532_frame	**fp;		/* in/out */
	db_addr_t		*ip;		/* out */
	int			*argp;		/* in */
	int			is_trap;	/* in */
	void			(*pr) __P((const char *, ...));
{
	struct trapframe *tf;
	struct syscframe *sf;

	switch (is_trap) {
	case INTERRUPT:
		(*pr)("--- interrupt ---\n");
	case NONE:
		*ip = (db_addr_t)
		      db_get_value((int) &(*fp)->f_retaddr, 4, FALSE);
		*fp = (struct ns532_frame *)
		      db_get_value((int) &(*fp)->f_frame, 4, FALSE);
		break;

	/* The only argument to trap() or syscall() is the trapframe. */
	case TRAP:
		tf = (struct trapframe *)argp;
		(*pr)("--- trap (number %ld) ---\n", tf->tf_trapno);
		*fp = (struct ns532_frame *)tf->tf_regs.r_fp;
		*ip = (db_addr_t)tf->tf_regs.r_pc;
		break;

	case SYSCALL:
		sf = (struct syscframe *)argp;
		(*pr)("--- syscall (number %d) ---\n", sf->sf_regs.r_r0);
		*fp = (struct ns532_frame *)sf->sf_regs.r_fp;
		*ip = (db_addr_t)sf->sf_regs.r_pc;
		break;
	}
}

void
db_stack_trace_print(addr, have_addr, count, modif, pr)
	db_expr_t	addr;
	boolean_t	have_addr;
	db_expr_t	count;
	char		*modif;
	void		(*pr) __P((const char *, ...));
{
	struct ns532_frame *frame, *lastframe;
	int		*argp;
	db_addr_t	callpc;
	int		is_trap = 0;
	boolean_t	kernel_only = TRUE;
	boolean_t	trace_thread = FALSE;

	{
		char *cp = modif;
		char c;

		while ((c = *cp++) != 0) {
			if (c == 't')
				trace_thread = TRUE;
			if (c == 'u')
				kernel_only = FALSE;
		}
	}

	if (!have_addr) {
		frame = (struct ns532_frame *)ddb_regs.tf_regs.r_fp;
		callpc = (db_addr_t)ddb_regs.tf_regs.r_pc;
	} else if (trace_thread) {
		(*pr) ("db_trace.c: can't trace thread\n");
	} else {
		frame = (struct ns532_frame *)addr;
		callpc = (db_addr_t)
			db_get_value((int)&frame->f_retaddr, 4, FALSE);
	}

	lastframe = 0;
	while (count && frame != 0) {
		int		narg;
		char *	name;
		db_expr_t	offset;
		db_sym_t	sym;
#define MAXNARG	16
		char	*argnames[MAXNARG], **argnp = NULL;

		sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
		db_symbol_values(sym, &name, NULL);

		if (lastframe == 0 && sym == NULL) {
			/* Symbol not found, peek at code */
			int	instr = db_get_value(callpc, 1, FALSE);

			offset = 1;
			if ((instr & 0xff) == 0x82) /* enter [],c */
				offset = 0;
		}
		if (INKERNEL((int)frame) && name) {
			if (!strcmp(name, "_trap")) {
				is_trap = TRAP;
			} else if (!strcmp(name, "_syscall")) {
				is_trap = SYSCALL;
			} else if (!strcmp(name, "_interrupt")) {
				is_trap = INTERRUPT;
			} else
				goto normal;
			narg = 0;
		} else {
		normal:
			is_trap = NONE;
			narg = MAXNARG;
			if (db_sym_numargs(sym, &narg, argnames))
				argnp = argnames;
			else
				narg = db_numargs(frame);
		}

		(*pr)("%s(", name);

		if (lastframe == 0 && offset == 0 && !have_addr) {
			/*
			 * We have a breakpoint before the frame is set up
			 * Use sp instead
			 */
			argp = &((struct ns532_frame *)(ddb_regs.tf_regs.r_sp-4))->f_arg0;
		} else {
			argp = &frame->f_arg0;
		}

		while (narg) {
			if (argnp)
				(*pr)("%s=", *argnp++);
			(*pr)("%lx", db_get_value((int)argp, 4, FALSE));
			argp++;
			if (--narg != 0)
				(*pr)(",");
		}
		(*pr)(") at ");
		db_printsym(callpc, DB_STGY_PROC, pr);
		(*pr)("\n");

		if (lastframe == 0 && offset == 0 && !have_addr) {
			/* Frame really belongs to next callpc */
			lastframe = (struct ns532_frame *)(ddb_regs.tf_regs.r_sp-4);
			callpc = (db_addr_t)
				 db_get_value((int)&lastframe->f_retaddr, 4, FALSE);
			continue;
		}

		lastframe = frame;
		db_nextframe(&frame, &callpc, &frame->f_arg0, is_trap, pr);

		if (frame == 0) {
			/* end of chain */
			break;
		}
		if (INKERNEL((int)frame)) {
			/* staying in kernel */
			if (frame <= lastframe) {
				(*pr)("Bad frame pointer: %p\n", frame);
				break;
			}
		} else if (INKERNEL((int)lastframe)) {
			/* switch from user to kernel */
			if (kernel_only)
				break;	/* kernel stack only */
		} else {
			/* in user */
			if (frame <= lastframe) {
				(*pr)("Bad user frame pointer: %p\n",
					  frame);
				break;
			}
		}
		--count;
	}

	if (count && is_trap != NONE) {
		db_printsym(callpc, DB_STGY_XTRN, pr);
		(*pr)(":\n");
	}
}

/**********************************************

  Get/Set value of special registers.

  *********************************************/

static int
db_spec_regs(vp, valp, what, pr)
	struct db_variable *vp;
	db_expr_t *valp;
	int what;
	void (*pr) __P((const char *, ...));
{
	if (strcmp(vp->name, "intbase") == 0)
		if (what == DB_VAR_GET)
			sprd(intbase, *valp);
		else
			lprd(intbase, *valp);
	else if (strcmp(vp->name, "ptb") == 0)
		if (what == DB_VAR_GET)
			smr(ptb0, *valp);
		else
			load_ptb(*valp);
	else if (strcmp(vp->name, "ivar") == 0)
		if (what == DB_VAR_GET)
			*valp = 0;
		else {
			lmr(ivar0, *valp);
			lmr(ivar1, *valp);
		}
	else if (strcmp(vp->name, "rtear") == 0)
		if (what == DB_VAR_GET)
			smr(tear, *valp);
		else
			lmr(tear, *valp);
	else if (strcmp(vp->name, "mcr") == 0)
		if (what == DB_VAR_GET)
			smr(mcr, *valp);
		else
			lmr(mcr, *valp);
	else if (strcmp(vp->name, "rmsr") == 0)
		if (what == DB_VAR_GET)
			smr(msr, *valp);
		else
			lmr(msr, *valp);
	else if (strcmp(vp->name, "dcr") == 0)
		if (what == DB_VAR_GET)
			sprd(dcr, *valp);
		else
			lprd(dcr, *valp);
	else if (strcmp(vp->name, "dsr") == 0)
		if (what == DB_VAR_GET)
			sprd(dsr, *valp);
		else
			lprd(dsr, *valp);
	else if (strcmp(vp->name, "car") == 0)
		if (what == DB_VAR_GET)
			sprd(car, *valp);
		else
			lprd(car, *valp);
	else if (strcmp(vp->name, "bpc") == 0)
		if (what == DB_VAR_GET)
			sprd(bpc, *valp);
		else
			lprd(bpc, *valp);
	else if (strcmp(vp->name, "cfg") == 0)
		if (what == DB_VAR_GET)
			sprd(cfg, *valp);
		else
			lprd(cfg, *valp);
	else
		(*pr)("Internal error, unknown register in db_spec_regs");
	return(0);
}