diff options
| -rw-r--r-- | sys/alpha/alpha/db_trace.c | 378 | ||||
| -rw-r--r-- | sys/alpha/alpha/exception.s | 12 | ||||
| -rw-r--r-- | sys/alpha/include/asm.h | 4 |
3 files changed, 325 insertions, 69 deletions
diff --git a/sys/alpha/alpha/db_trace.c b/sys/alpha/alpha/db_trace.c index 2df25c15678cb..a459b5c1ebf18 100644 --- a/sys/alpha/alpha/db_trace.c +++ b/sys/alpha/alpha/db_trace.c @@ -1,14 +1,58 @@ /* $FreeBSD$ */ -/* $NetBSD: db_trace.c,v 1.1 1997/09/06 02:00:50 thorpej Exp $ */ +/* $NetBSD: db_trace.c,v 1.9 2000/12/13 03:16:36 mycroft Exp $ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Ross Harvey. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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/cdefs.h> /* RCS ID & Copyright macro defns */ -/* __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.1 1997/09/06 02:00:50 thorpej Exp $"); */ +/*__KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.9 2000/12/13 03:16:36 mycroft Exp $");*/ #include <sys/param.h> +#include <sys/systm.h> #include <sys/proc.h> +#include <sys/user.h> +#include <sys/sysent.h> #include <machine/db_machdep.h> + #include <ddb/ddb.h> #include <ddb/db_sym.h> #include <ddb/db_access.h> @@ -16,93 +60,305 @@ #include <ddb/db_output.h> #include <alpha/alpha/db_instruction.h> -struct alpha_proc { - int pcreg; /* index of return pc register */ - int frame_size; /* size of stack frame */ - u_int32_t reg_mask; - int regs[32]; /* offsets from sp to saved regs */ +/* + * Information about the `standard' Alpha function prologue. + */ +struct prologue_info { + int pi_reg_offset[32]; /* offset of registers in stack frame */ + u_int32_t pi_regmask; /* which registers are in frame */ + int pi_frame_size; /* frame size */ +}; + +/* + * We use several symbols to take special action: + * + * Trap vectors, which use a different (fixed-size) stack frame: + * + * XentArith + * XentIF + * XentInt + * XentMM + * XentSys + * XentUna + */ + +static struct special_symbol { + uintptr_t ss_val; + const char *ss_note; +} special_symbols[] = { + { (uintptr_t)&XentArith, "arithmetic trap" }, + { (uintptr_t)&XentIF, "instruction fault" }, + { (uintptr_t)&XentInt, "interrupt" }, + { (uintptr_t)&XentMM, "memory management fault" }, + { (uintptr_t)&XentSys, "syscall" }, + { (uintptr_t)&XentUna, "unaligned access fault" }, + { (uintptr_t)&XentRestart, "console restart" }, + { NULL } }; +/* + * Decode the function prologue for the function we're in, and note + * which registers are stored where, and how large the stack frame is. + */ static void -parse_proc(db_expr_t addr, struct alpha_proc* frame) +decode_prologue(db_addr_t callpc, db_addr_t func, + struct prologue_info *pi) { - c_db_sym_t sym; - db_expr_t func; - db_expr_t junk, pc, limit; + long signed_immediate; + alpha_instruction ins; + db_expr_t pc; - frame->pcreg = -1; - frame->reg_mask = 0; - frame->frame_size = 0; + pi->pi_regmask = 0; + pi->pi_frame_size = 0; - sym = db_search_symbol(addr, DB_STGY_PROC, &junk); - if (!sym) - return; - db_symbol_values(sym, 0, &func); +#define CHECK_FRAMESIZE \ +do { \ + if (pi->pi_frame_size != 0) { \ + printf("frame size botch: adjust register offsets?\n"); \ + } \ +} while (0) - pc = func; - limit = addr; - if (limit - pc > 200) - limit = pc + 200; - for (; pc < limit; pc += 4) { - alpha_instruction ins; - ins.bits = *(u_int32_t*) pc; - if (ins.memory_format.opcode == op_lda - && ins.memory_format.ra == 30) { - /* gcc 2.7 */ - frame->frame_size += -ins.memory_format.offset; - } else if (ins.operate_lit_format.opcode == op_arit - && ins.operate_lit_format.function == op_subq - && ins.operate_lit_format.rs == 30) { - /* egcs */ - frame->frame_size += ins.operate_lit_format.literal; - } else if (ins.memory_format.opcode == op_stq - && ins.memory_format.rb == 30 - && ins.memory_format.ra != 31) { - int reg = ins.memory_format.ra; - frame->reg_mask |= 1 << reg; - frame->regs[reg] = ins.memory_format.offset; - if (frame->pcreg == -1 && reg == 26) - frame->pcreg = reg; + for (pc = func; pc < callpc; pc += sizeof(alpha_instruction)) { + ins.bits = *(unsigned int *)pc; + + if (ins.memory_format.opcode == op_lda && + ins.memory_format.ra == 30 && + ins.memory_format.rb == 30) { + /* + * GCC 2.7-style stack adjust: + * + * lda sp, -64(sp) + */ + signed_immediate = (long)ins.mem_format.displacement; +#if 1 + if (signed_immediate > 0) + printf("prologue botch: displacement %ld\n", + signed_immediate); +#endif + CHECK_FRAMESIZE; + pi->pi_frame_size += -signed_immediate; + } else if (ins.operate_lit_format.opcode == op_arit && + ins.operate_lit_format.function == op_subq && + ins.operate_lit_format.rs == 30 && + ins.operate_lit_format.rd == 30) { + /* + * EGCS-style stack adjust: + * + * subq sp, 64, sp + */ + CHECK_FRAMESIZE; + pi->pi_frame_size += ins.operate_lit_format.literal; + } else if (ins.mem_format.opcode == op_stq && + ins.mem_format.rs == 30 && + ins.mem_format.rd != 31) { + /* Store of (non-zero) register onto the stack. */ + signed_immediate = (long)ins.mem_format.displacement; + pi->pi_regmask |= 1 << ins.mem_format.rd; + pi->pi_reg_offset[ins.mem_format.rd] = signed_immediate; } } } +static int +sym_is_trapsymbol(uintptr_t v) +{ + int i; + + for (i = 0; special_symbols[i].ss_val != NULL; ++i) + if (v == special_symbols[i].ss_val) + return 1; + return 0; +} + +static void +decode_syscall(int number, struct proc *p) +{ + c_db_sym_t sym; + db_expr_t diff; + sy_call_t *f; + const char *symname; + + printf(" (%d", number); + if (!p) + goto out; + + if (0 <= number && number < p->p_sysent->sv_size) { + + f = p->p_sysent->sv_table[number].sy_call; + sym = db_search_symbol((db_addr_t)f, DB_STGY_ANY, &diff); + if (sym == DB_SYM_NULL || diff != 0) + goto out; + db_symbol_values(sym, &symname, NULL); + printf(", %s, %s", p->p_sysent->sv_name, symname); + } + out: + printf(")"); + +} + void db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif) { - db_addr_t callpc; - db_addr_t frame; + db_addr_t callpc = 0, frame = 0, symval; + struct prologue_info pi; + db_expr_t diff; + c_db_sym_t sym; + int i; + u_long tfps; + const char *symname; + struct pcb *pcbp; + char c, *cp = modif; + struct trapframe *tf = NULL; + boolean_t ra_from_tf = FALSE; + boolean_t ra_from_pcb; + u_long last_ipl = ~0L; + struct proc *p = NULL; + boolean_t trace_thread = FALSE; + boolean_t have_trapframe = FALSE; + + while ((c = *cp++) != 0) + trace_thread |= c == 't'; if (count == -1) count = 65535; if (!have_addr) { - frame = (db_addr_t) ddb_regs.tf_regs[FRAME_SP]; - callpc = (db_addr_t)ddb_regs.tf_regs[FRAME_PC]; + p = curproc; + addr = DDB_REGS->tf_regs[FRAME_SP] - FRAME_SIZE * 8; + tf = (struct trapframe *)addr; + have_trapframe = 1; } else { - frame = (db_addr_t)addr; - callpc = (db_addr_t)db_get_value(frame, 8, FALSE); + if (trace_thread) { + printf("trace: pid %d ", (int)addr); + p = pfind(addr); + if (p == NULL) { + printf("not found\n"); + return; + } + if ((p->p_sflag & PS_INMEM) == 0) { + printf("swapped out\n"); + return; + } + pcbp = &p->p_addr->u_pcb; + addr = (db_expr_t)pcbp->pcb_hw.apcb_ksp; + callpc = pcbp->pcb_context[7]; + printf("at 0x%lx\n", addr); + } else { + printf("alpha trace requires known PC =eject=\n"); + return; + } + frame = addr; } while (count--) { - const char * name; - db_expr_t offset; - c_db_sym_t sym; - struct alpha_proc proc; + if (have_trapframe) { + frame = (db_addr_t)tf + FRAME_SIZE * 8; + callpc = tf->tf_regs[FRAME_PC]; + ra_from_tf = TRUE; + have_trapframe = 0; + } + sym = db_search_symbol(callpc, DB_STGY_ANY, &diff); + if (sym == DB_SYM_NULL) + break; - sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); - db_symbol_values(sym, &name, NULL); + db_symbol_values(sym, &symname, (db_expr_t *)&symval); + + if (callpc < symval) { + printf("symbol botch: callpc 0x%lx < " + "func 0x%lx (%s)\n", callpc, symval, symname); + return; + } - db_printf("%s() at ", name); + /* + * XXX Printing out arguments is Hard. We'd have to + * keep lots of state as we traverse the frame, figuring + * out where the arguments to the function are stored + * on the stack. + * + * Even worse, they may be stored to the stack _after_ + * being modified in place; arguments are passed in + * registers. + * + * So, in order for this to work reliably, we pretty much + * have to have a kernel built with `cc -g': + * + * - The debugging symbols would tell us where the + * arguments are, how many there are, if there were + * any passed on the stack, etc. + * + * - Presumably, the compiler would be careful to + * store the argument registers on the stack before + * modifying the registers, so that a debugger could + * know what those values were upon procedure entry. + * + * Because of this, we don't bother. We've got most of the + * benefit of back tracking without the arguments, and we + * could get the arguments if we use a remote source-level + * debugger (for serious debugging). + */ + printf("%s() at ", symname); db_printsym(callpc, DB_STGY_PROC); - db_printf("\n"); + printf("\n"); - parse_proc(callpc, &proc); + /* + * If we are in a trap vector, frame points to a + * trapframe. + */ + if (sym_is_trapsymbol(symval)) { + tf = (struct trapframe *)frame; - if (proc.pcreg == -1) - break; + for (i = 0; special_symbols[i].ss_val != NULL; ++i) + if (symval == special_symbols[i].ss_val) + printf("--- %s", + special_symbols[i].ss_note); + + tfps = tf->tf_regs[FRAME_PS]; + if (symval == (uintptr_t)&XentSys) + decode_syscall(tf->tf_regs[FRAME_V0], p); + if ((tfps & ALPHA_PSL_IPL_MASK) != last_ipl) { + last_ipl = tfps & ALPHA_PSL_IPL_MASK; + if (symval != (uintptr_t)&XentSys) + printf(" (from ipl %ld)", last_ipl); + } + printf(" ---\n"); + if (tfps & ALPHA_PSL_USERMODE) { + printf("--- user mode ---\n"); + break; /* Terminate search. */ + } + have_trapframe = 1; + continue; + } - callpc = db_get_value(frame + proc.regs[proc.pcreg], 8, FALSE); - frame = frame + proc.frame_size; + /* + * This is a bit trickier; we must decode the function + * prologue to find the saved RA. + * + * XXX How does this interact w/ alloca()?! + */ + decode_prologue(callpc, symval, &pi); + if ((pi.pi_regmask & (1 << 26)) == 0) { + /* + * No saved RA found. We might have RA from + * the trap frame, however (e.g trap occurred + * in a leaf call). If not, we've found the + * root of the call graph. + */ + if (ra_from_tf) + callpc = tf->tf_regs[FRAME_RA]; + else { + printf("--- root of call graph ---\n"); + break; + } + } else + callpc = *(u_long *)(frame + pi.pi_reg_offset[26]); + ra_from_tf = ra_from_pcb = FALSE; +#if 0 + /* + * The call was actually made at RA - 4; the PC is + * updated before being stored in RA. + */ + callpc -= 4; +#endif + frame += pi.pi_frame_size; } } diff --git a/sys/alpha/alpha/exception.s b/sys/alpha/alpha/exception.s index 31665c898e553..c7119b0d49f6b 100644 --- a/sys/alpha/alpha/exception.s +++ b/sys/alpha/alpha/exception.s @@ -125,8 +125,8 @@ /* syscall number, passed in v0, is first arg, frame pointer second */ mov v0,a0 mov sp,a1 ; .loc 1 __LINE__ - br pv,XentSys1 -XentSys1: LDGP(pv) + br pv,1f +1: LDGP(pv) CALL(syscall) /* Handle any AST's. */ @@ -192,8 +192,8 @@ LEAF(XentUna, 3) /* XXX should be NESTED */ /* a0, a1, & a2 already set up */ ldiq a3, ALPHA_KENTRY_UNA mov sp, a4 ; .loc 1 __LINE__ - br pv, XentUna1 -XentUna1: LDGP(pv) + br pv, 1f +1: LDGP(pv) CALL(trap) jmp zero, exception_return @@ -236,8 +236,8 @@ LEAF(XentRestart, 1) /* XXX should be NESTED */ stq t12,(FRAME_T12*8)(sp) stq ra,(FRAME_RA*8)(sp) - br pv,LXconsole_restart1 -LXconsole_restart1: LDGP(pv) + br pv,1f +1: LDGP(pv) ldq a0,(FRAME_RA*8)(sp) /* a0 = ra */ ldq a1,(FRAME_T11*8)(sp) /* a1 = ai */ diff --git a/sys/alpha/include/asm.h b/sys/alpha/include/asm.h index b6cc41ef83820..4a9963c5a40b9 100644 --- a/sys/alpha/include/asm.h +++ b/sys/alpha/include/asm.h @@ -256,8 +256,8 @@ #define PALVECT(_name_) \ ESETUP(_name_); \ ERSAVE(); \ - br pv, _name_##lgp; \ -_name_##lgp:; \ + br pv, 1001f; \ +1001:; \ LDGP(pv) #define ESETUP(_name_) \ |
