Index: head/sys/mips/mips/db_trace.c =================================================================== --- head/sys/mips/mips/db_trace.c (revision 302602) +++ head/sys/mips/mips/db_trace.c (revision 302603) @@ -1,466 +1,479 @@ /*- * Copyright (c) 2004-2005, Juniper Networks, Inc. * 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 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 AUTHOR 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. * * JNPR: db_trace.c,v 1.8 2007/08/09 11:23:32 katta */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include extern char _locore[]; extern char _locoreEnd[]; extern char edata[]; /* * A function using a stack frame has the following instruction as the first * one: [d]addiu sp,sp,- * * We make use of this to detect starting address of a function. This works * better than using 'j ra' instruction to signify end of the previous * function (for e.g. functions like boot() or panic() do not actually * emit a 'j ra' instruction). * * XXX the abi does not require that the addiu instruction be the first one. */ #define MIPS_START_OF_FUNCTION(ins) ((((ins) & 0xffff8000) == 0x27bd8000) \ || (((ins) & 0xffff8000) == 0x67bd8000)) /* * MIPS ABI 3.0 requires that all functions return using the 'j ra' instruction * * XXX gcc doesn't do this for functions with __noreturn__ attribute. */ #define MIPS_END_OF_FUNCTION(ins) ((ins) == 0x03e00008) #if defined(__mips_n64) # define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ ((vm_offset_t)(reg) >= MIPS_XKPHYS_START)) #else # define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ ((vm_offset_t)(reg) >= MIPS_KSEG0_START)) #endif /* * Functions ``special'' enough to print by name */ #ifdef __STDC__ #define Name(_fn) { (void*)_fn, # _fn } #else #define Name(_fn) { _fn, "_fn"} #endif static struct { void *addr; char *name; } names[] = { Name(trap), Name(MipsKernGenException), Name(MipsUserGenException), Name(MipsKernIntr), Name(MipsUserIntr), Name(cpu_switch), { 0, 0 } }; /* * Map a function address to a string name, if known; or a hex string. */ static char * fn_name(uintptr_t addr) { static char buf[17]; int i = 0; db_expr_t diff; c_db_sym_t sym; char *symname; diff = 0; symname = NULL; sym = db_search_symbol((db_addr_t)addr, DB_STGY_ANY, &diff); db_symbol_values(sym, (const char **)&symname, (db_expr_t *)0); if (symname && diff == 0) return (symname); for (i = 0; names[i].name; i++) if (names[i].addr == (void *)addr) return (names[i].name); sprintf(buf, "%jx", (uintmax_t)addr); return (buf); } void stacktrace_subr(register_t pc, register_t sp, register_t ra, int (*printfn) (const char *,...)) { InstFmt i; /* * Arrays for a0..a3 registers and flags if content * of these registers is valid, e.g. obtained from the stack */ int valid_args[4]; uintptr_t args[4]; uintptr_t va, subr; unsigned instr, mask; unsigned int frames = 0; int more, stksize, j; + register_t next_ra; /* Jump here when done with a frame, to start a new one */ loop: /* * Invalidate arguments values */ valid_args[0] = 0; valid_args[1] = 0; valid_args[2] = 0; valid_args[3] = 0; + next_ra = 0; /* Jump here after a nonstandard (interrupt handler) frame */ stksize = 0; subr = 0; if (frames++ > 100) { (*printfn) ("\nstackframe count exceeded\n"); /* return breaks stackframe-size heuristics with gcc -O2 */ goto finish; /* XXX */ } /* check for bad SP: could foul up next frame */ /*XXX MIPS64 bad: this hard-coded SP is lame */ if (!MIPS_IS_VALID_KERNELADDR(sp)) { (*printfn) ("SP 0x%jx: not in kernel\n", sp); ra = 0; subr = 0; goto done; } #define Between(x, y, z) \ ( ((x) <= (y)) && ((y) < (z)) ) #define pcBetween(a,b) \ Between((uintptr_t)a, pc, (uintptr_t)b) /* * Check for current PC in exception handler code that don't have a * preceding "j ra" at the tail of the preceding function. Depends * on relative ordering of functions in exception.S, swtch.S. */ if (pcBetween(MipsKernGenException, MipsUserGenException)) subr = (uintptr_t)MipsKernGenException; else if (pcBetween(MipsUserGenException, MipsKernIntr)) subr = (uintptr_t)MipsUserGenException; else if (pcBetween(MipsKernIntr, MipsUserIntr)) subr = (uintptr_t)MipsKernIntr; else if (pcBetween(MipsUserIntr, MipsTLBInvalidException)) subr = (uintptr_t)MipsUserIntr; else if (pcBetween(MipsTLBInvalidException, MipsTLBMissException)) subr = (uintptr_t)MipsTLBInvalidException; else if (pcBetween(fork_trampoline, savectx)) subr = (uintptr_t)fork_trampoline; else if (pcBetween(savectx, cpu_throw)) subr = (uintptr_t)savectx; else if (pcBetween(cpu_throw, cpu_switch)) subr = (uintptr_t)cpu_throw; else if (pcBetween(cpu_switch, MipsSwitchFPState)) subr = (uintptr_t)cpu_switch; else if (pcBetween(_locore, _locoreEnd)) { subr = (uintptr_t)_locore; ra = 0; goto done; } /* check for bad PC */ /*XXX MIPS64 bad: These hard coded constants are lame */ if (!MIPS_IS_VALID_KERNELADDR(pc)) { (*printfn) ("PC 0x%jx: not in kernel\n", pc); ra = 0; goto done; } /* * Find the beginning of the current subroutine by scanning * backwards from the current PC for the end of the previous * subroutine. */ if (!subr) { va = pc - sizeof(int); while (1) { instr = kdbpeek((int *)va); if (MIPS_START_OF_FUNCTION(instr)) break; if (MIPS_END_OF_FUNCTION(instr)) { /* skip over branch-delay slot instruction */ va += 2 * sizeof(int); break; } va -= sizeof(int); } /* skip over nulls which might separate .o files */ while ((instr = kdbpeek((int *)va)) == 0) va += sizeof(int); subr = va; } /* scan forwards to find stack size and any saved registers */ stksize = 0; more = 3; mask = 0; for (va = subr; more; va += sizeof(int), more = (more == 3) ? 3 : more - 1) { /* stop if hit our current position */ if (va >= pc) break; instr = kdbpeek((int *)va); i.word = instr; switch (i.JType.op) { case OP_SPECIAL: switch (i.RType.func) { case OP_JR: case OP_JALR: more = 2; /* stop after next instruction */ break; case OP_SYSCALL: case OP_BREAK: more = 1; /* stop now */ } break; case OP_BCOND: case OP_J: case OP_JAL: case OP_BEQ: case OP_BNE: case OP_BLEZ: case OP_BGTZ: more = 2; /* stop after next instruction */ break; case OP_COP0: case OP_COP1: case OP_COP2: case OP_COP3: switch (i.RType.rs) { case OP_BCx: case OP_BCy: more = 2; /* stop after next instruction */ } break; case OP_SW: /* look for saved registers on the stack */ if (i.IType.rs != 29) break; - /* only restore the first one */ - if (mask & (1 << i.IType.rt)) + /* + * only restore the first one except RA for + * MipsKernGenException case + */ + if (mask & (1 << i.IType.rt)) { + if (subr == (uintptr_t)MipsKernGenException && + i.IType.rt == 31) + next_ra = kdbpeek((int *)(sp + + (short)i.IType.imm)); break; + } mask |= (1 << i.IType.rt); switch (i.IType.rt) { case 4:/* a0 */ args[0] = kdbpeek((int *)(sp + (short)i.IType.imm)); valid_args[0] = 1; break; case 5:/* a1 */ args[1] = kdbpeek((int *)(sp + (short)i.IType.imm)); valid_args[1] = 1; break; case 6:/* a2 */ args[2] = kdbpeek((int *)(sp + (short)i.IType.imm)); valid_args[2] = 1; break; case 7:/* a3 */ args[3] = kdbpeek((int *)(sp + (short)i.IType.imm)); valid_args[3] = 1; break; case 31: /* ra */ ra = kdbpeek((int *)(sp + (short)i.IType.imm)); } break; case OP_SD: /* look for saved registers on the stack */ if (i.IType.rs != 29) break; /* only restore the first one */ if (mask & (1 << i.IType.rt)) break; mask |= (1 << i.IType.rt); switch (i.IType.rt) { case 4:/* a0 */ args[0] = kdbpeekd((int *)(sp + (short)i.IType.imm)); valid_args[0] = 1; break; case 5:/* a1 */ args[1] = kdbpeekd((int *)(sp + (short)i.IType.imm)); valid_args[1] = 1; break; case 6:/* a2 */ args[2] = kdbpeekd((int *)(sp + (short)i.IType.imm)); valid_args[2] = 1; break; case 7:/* a3 */ args[3] = kdbpeekd((int *)(sp + (short)i.IType.imm)); valid_args[3] = 1; break; case 31: /* ra */ ra = kdbpeekd((int *)(sp + (short)i.IType.imm)); } break; case OP_ADDI: case OP_ADDIU: case OP_DADDI: case OP_DADDIU: /* look for stack pointer adjustment */ if (i.IType.rs != 29 || i.IType.rt != 29) break; stksize = -((short)i.IType.imm); } } done: (*printfn) ("%s+%x (", fn_name(subr), pc - subr); for (j = 0; j < 4; j ++) { if (j > 0) (*printfn)(","); if (valid_args[j]) (*printfn)("%x", args[j]); else (*printfn)("?"); } - (*printfn) (") ra %jx sp %jx sz %d\n", ra, sp, stksize); + (*printfn) (") ra %jx sp %jx sz %d\n", + (uintmax_t)(u_register_t) ra, + (uintmax_t)(u_register_t) sp, + stksize); if (ra) { if (pc == ra && stksize == 0) (*printfn) ("stacktrace: loop!\n"); else { pc = ra; sp += stksize; - ra = 0; + ra = next_ra; goto loop; } } else { finish: if (curproc) (*printfn) ("pid %d\n", curproc->p_pid); else (*printfn) ("curproc NULL\n"); } } int db_md_set_watchpoint(db_expr_t addr, db_expr_t size) { return(0); } int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) { return(0); } void db_md_list_watchpoints() { } void db_trace_self(void) { db_trace_thread (curthread, -1); return; } int db_trace_thread(struct thread *thr, int count) { register_t pc, ra, sp; struct pcb *ctx; if (thr == curthread) { sp = (register_t)(intptr_t)__builtin_frame_address(0); ra = (register_t)(intptr_t)__builtin_return_address(0); __asm __volatile( "jal 99f\n" "nop\n" "99:\n" "move %0, $31\n" /* get ra */ "move $31, %1\n" /* restore ra */ : "=r" (pc) : "r" (ra)); } else { ctx = kdb_thr_ctx(thr); sp = (register_t)ctx->pcb_context[PCB_REG_SP]; pc = (register_t)ctx->pcb_context[PCB_REG_PC]; ra = (register_t)ctx->pcb_context[PCB_REG_RA]; } stacktrace_subr(pc, sp, ra, (int (*) (const char *, ...))db_printf); return (0); } void db_show_mdpcpu(struct pcpu *pc) { db_printf("ipis = 0x%x\n", pc->pc_pending_ipis); db_printf("next ASID = %d\n", pc->pc_next_asid); db_printf("GENID = %d\n", pc->pc_asid_generation); return; }