diff --git a/sys/alpha/alpha/db_trace.c b/sys/alpha/alpha/db_trace.c index 79ed437e0749..615bd2edff28 100644 --- a/sys/alpha/alpha/db_trace.c +++ b/sys/alpha/alpha/db_trace.c @@ -1,386 +1,372 @@ /* $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 /* RCS ID & Copyright macro defns */ /*__KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.9 2000/12/13 03:16:36 mycroft Exp $");*/ __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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" }, { 0, NULL } }; int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); void db_md_list_watchpoints(void); /* * 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 int decode_prologue(db_addr_t callpc, db_addr_t func, struct prologue_info *pi) { long signed_immediate; alpha_instruction ins; db_expr_t pc; pi->pi_regmask = 0; pi->pi_frame_size = 0; #define CHECK_FRAMESIZE \ do { \ if (pi->pi_frame_size != 0) { \ db_printf("frame size botch: adjust register offsets?\n"); \ return (1); \ } \ } while (0) 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) { db_printf("prologue botch: displacement %ld\n", signed_immediate); return (1); } #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; } } return (0); } static int sym_is_trapsymbol(uintptr_t v) { int i; for (i = 0; special_symbols[i].ss_val != 0; ++i) if (v == special_symbols[i].ss_val) return 1; return 0; } static void decode_syscall(int number, struct thread *td) { struct proc *p; c_db_sym_t sym; db_expr_t diff; sy_call_t *f; const char *symname; p = (td != NULL) ? td->td_proc : NULL; db_printf(" (%d", number); if (p != NULL && 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) { db_symbol_values(sym, &symname, NULL); db_printf(", %s, %s", p->p_sysent->sv_name, symname); } } db_printf(")"); } static int db_backtrace(struct thread *td, db_addr_t frame, db_addr_t pc, int count) { struct prologue_info pi; struct trapframe *tf; const char *symname; c_db_sym_t sym; db_expr_t diff; db_addr_t symval; u_long last_ipl, tfps; int i; if (count == -1) count = 1024; last_ipl = ~0L; tf = NULL; while (count--) { sym = db_search_symbol(pc, DB_STGY_ANY, &diff); if (sym == DB_SYM_NULL) return (ENOENT); db_symbol_values(sym, &symname, (db_expr_t *)&symval); if (pc < symval) { db_printf("symbol botch: pc 0x%lx < " "func 0x%lx (%s)\n", pc, symval, symname); return (0); } /* * 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). */ db_printf("%s() at ", symname); db_printsym(pc, DB_STGY_PROC); db_printf("\n"); /* * If we are in a trap vector, frame points to a * trapframe. */ if (sym_is_trapsymbol(symval)) { tf = (struct trapframe *)frame; for (i = 0; special_symbols[i].ss_val != 0; ++i) if (symval == special_symbols[i].ss_val) db_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], td); if ((tfps & ALPHA_PSL_IPL_MASK) != last_ipl) { last_ipl = tfps & ALPHA_PSL_IPL_MASK; if (symval != (uintptr_t)&XentSys) db_printf(" (from ipl %ld)", last_ipl); } db_printf(" ---\n"); if (tfps & ALPHA_PSL_USERMODE) { db_printf("--- user mode ---\n"); break; /* Terminate search. */ } frame = (db_addr_t)(tf + 1); pc = tf->tf_regs[FRAME_PC]; continue; } /* * This is a bit trickier; we must decode the function * prologue to find the saved RA. * * XXX How does this interact w/ alloca()?! */ if (decode_prologue(pc, symval, &pi)) return (0); 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 (tf) pc = tf->tf_regs[FRAME_RA]; else { db_printf("--- root of call graph ---\n"); break; } } else pc = *(u_long *)(frame + pi.pi_reg_offset[26]); frame += pi.pi_frame_size; tf = NULL; } return (0); } -void -db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, - char *modif) -{ - struct thread *td; - - td = (have_addr) ? kdb_thr_lookup(addr) : kdb_thread; - if (td == NULL) { - db_printf("Thread %d not found\n", (int)addr); - return; - } - db_trace_thread(td, count); -} - void db_trace_self(void) { register_t pc, sp; __asm __volatile( " mov $30,%0 \n" " lda %1,1f \n" "1:\n" : "=r" (sp), "=r" (pc)); db_backtrace(curthread, sp, pc, -1); } int db_trace_thread(struct thread *thr, int count) { struct pcb *ctx; ctx = kdb_thr_ctx(thr); return (db_backtrace(thr, ctx->pcb_hw.apcb_ksp, ctx->pcb_context[7], count)); } int db_md_set_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { return (-1); } int db_md_clr_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { return (-1); } void db_md_list_watchpoints() { return; } diff --git a/sys/amd64/amd64/db_trace.c b/sys/amd64/amd64/db_trace.c index 03842cb530eb..5928dbe48d68 100644 --- a/sys/amd64/amd64/db_trace.c +++ b/sys/amd64/amd64/db_trace.c @@ -1,685 +1,671 @@ /* * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * 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 ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS 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 the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_dr0; static db_varfcn_t db_dr1; static db_varfcn_t db_dr2; static db_varfcn_t db_dr3; static db_varfcn_t db_dr4; static db_varfcn_t db_dr5; static db_varfcn_t db_dr6; static db_varfcn_t db_dr7; static db_varfcn_t db_frame; static db_varfcn_t db_rsp; static db_varfcn_t db_ss; /* * Machine register set. */ #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "cs", DB_OFFSET(tf_cs), db_frame }, #if 0 { "ds", DB_OFFSET(tf_ds), db_frame }, { "es", DB_OFFSET(tf_es), db_frame }, { "fs", DB_OFFSET(tf_fs), db_frame }, { "gs", DB_OFFSET(tf_gs), db_frame }, #endif { "ss", NULL, db_ss }, { "rax", DB_OFFSET(tf_rax), db_frame }, { "rcx", DB_OFFSET(tf_rcx), db_frame }, { "rdx", DB_OFFSET(tf_rdx), db_frame }, { "rbx", DB_OFFSET(tf_rbx), db_frame }, { "rsp", NULL, db_rsp }, { "rbp", DB_OFFSET(tf_rbp), db_frame }, { "rsi", DB_OFFSET(tf_rsi), db_frame }, { "rdi", DB_OFFSET(tf_rdi), db_frame }, { "r8", DB_OFFSET(tf_r8), db_frame }, { "r9", DB_OFFSET(tf_r9), db_frame }, { "r10", DB_OFFSET(tf_r10), db_frame }, { "r11", DB_OFFSET(tf_r11), db_frame }, { "r12", DB_OFFSET(tf_r12), db_frame }, { "r13", DB_OFFSET(tf_r13), db_frame }, { "r14", DB_OFFSET(tf_r14), db_frame }, { "r15", DB_OFFSET(tf_r15), db_frame }, { "rip", DB_OFFSET(tf_rip), db_frame }, { "rflags", DB_OFFSET(tf_rflags), db_frame }, { "dr0", NULL, db_dr0 }, { "dr1", NULL, db_dr1 }, { "dr2", NULL, db_dr2 }, { "dr3", NULL, db_dr3 }, { "dr4", NULL, db_dr4 }, { "dr5", NULL, db_dr5 }, { "dr6", NULL, db_dr6 }, { "dr7", NULL, db_dr7 }, }; struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); #define DB_DRX_FUNC(reg) \ static int \ db_ ## reg (vp, valuep, op) \ struct db_variable *vp; \ db_expr_t * valuep; \ int op; \ { \ if (op == DB_VAR_GET) \ *valuep = r ## reg (); \ else \ load_ ## reg (*valuep); \ return (1); \ } DB_DRX_FUNC(dr0) DB_DRX_FUNC(dr1) DB_DRX_FUNC(dr2) DB_DRX_FUNC(dr3) DB_DRX_FUNC(dr4) DB_DRX_FUNC(dr5) DB_DRX_FUNC(dr6) DB_DRX_FUNC(dr7) static __inline long get_rsp(struct trapframe *tf) { return ((ISPL(tf->tf_cs)) ? tf->tf_rsp : (db_expr_t)tf + offsetof(struct trapframe, tf_rsp)); } static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { long *reg; if (kdb_frame == NULL) return (0); reg = (long *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } static int db_rsp(struct db_variable *vp, db_expr_t *valuep, int op) { if (kdb_frame == NULL) return (0); if (op == DB_VAR_GET) *valuep = get_rsp(kdb_frame); else if (ISPL(kdb_frame->tf_cs)) kdb_frame->tf_rsp = *valuep; return (1); } static int db_ss(struct db_variable *vp, db_expr_t *valuep, int op) { if (kdb_frame == NULL) return (0); if (op == DB_VAR_GET) *valuep = (ISPL(kdb_frame->tf_cs)) ? kdb_frame->tf_ss : rss(); else if (ISPL(kdb_frame->tf_cs)) kdb_frame->tf_ss = *valuep; return (1); } /* * Stack trace. */ #define INKERNEL(va) (((vm_offset_t)(va)) >= USRSTACK) struct amd64_frame { struct amd64_frame *f_frame; long f_retaddr; long f_arg0; }; #define NORMAL 0 #define TRAP 1 #define INTERRUPT 2 #define SYSCALL 3 static void db_nextframe(struct amd64_frame **, db_addr_t *, struct thread *); static int db_numargs(struct amd64_frame *); static void db_print_stack_entry(const char *, int, char **, long *, db_addr_t); static void decode_syscall(int, struct thread *); static char * watchtype_str(int type); int amd64_set_watch(int watchnum, unsigned int watchaddr, int size, int access, struct dbreg * d); int amd64_clr_watch(int watchnum, struct dbreg * d); int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); void db_md_list_watchpoints(void); /* * Figure out how many arguments were passed into the frame at "fp". */ static int db_numargs(fp) struct amd64_frame *fp; { #if 1 return (0); /* regparm, needs dwarf2 info */ #else long *argp; int inst; int args; argp = (long *)db_get_value((long)&fp->f_retaddr, 8, FALSE); /* * XXX etext is wrong for LKMs. We should attempt to interpret * the instruction at the return address in all cases. This * may require better fault handling. */ if (argp < (long *)btext || argp >= (long *)etext) { args = 5; } else { inst = db_get_value((long)argp, 4, FALSE); if ((inst & 0xff) == 0x59) /* popl %ecx */ args = 1; else if ((inst & 0xffff) == 0xc483) /* addl $Ibs, %esp */ args = ((inst >> 16) & 0xff) / 4; else args = 5; } return (args); #endif } static void db_print_stack_entry(name, narg, argnp, argp, callpc) const char *name; int narg; char **argnp; long *argp; db_addr_t callpc; { db_printf("%s(", name); #if 0 while (narg) { if (argnp) db_printf("%s=", *argnp++); db_printf("%lr", (long)db_get_value((long)argp, 8, FALSE)); argp++; if (--narg != 0) db_printf(","); } #endif db_printf(") at "); db_printsym(callpc, DB_STGY_PROC); db_printf("\n"); } static void decode_syscall(int number, struct thread *td) { struct proc *p; c_db_sym_t sym; db_expr_t diff; sy_call_t *f; const char *symname; db_printf(" (%d", number); p = (td != NULL) ? td->td_proc : NULL; if (p != NULL && 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) { db_symbol_values(sym, &symname, NULL); db_printf(", %s, %s", p->p_sysent->sv_name, symname); } } db_printf(")"); } /* * Figure out the next frame up in the call stack. */ static void db_nextframe(struct amd64_frame **fp, db_addr_t *ip, struct thread *td) { struct trapframe *tf; int frame_type; long rip, rsp, rbp; db_expr_t offset; c_db_sym_t sym; const char *name; rip = db_get_value((long) &(*fp)->f_retaddr, 8, FALSE); rbp = db_get_value((long) &(*fp)->f_frame, 8, FALSE); /* * Figure out frame type. */ frame_type = NORMAL; sym = db_search_symbol(rip, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); if (name != NULL) { if (strcmp(name, "calltrap") == 0 || strcmp(name, "fork_trampoline") == 0) frame_type = TRAP; else if (strncmp(name, "Xatpic_intr", 11) == 0 || strncmp(name, "Xatpic_fastintr", 15) == 0 || strncmp(name, "Xapic_isr", 9) == 0) frame_type = INTERRUPT; else if (strcmp(name, "Xfast_syscall") == 0) frame_type = SYSCALL; } /* * Normal frames need no special processing. */ if (frame_type == NORMAL) { *ip = (db_addr_t) rip; *fp = (struct amd64_frame *) rbp; return; } db_print_stack_entry(name, 0, 0, 0, rip); /* * Point to base of trapframe which is just above the * current frame. */ tf = (struct trapframe *)((long)*fp + 16); if (INKERNEL((long) tf)) { rsp = get_rsp(tf); rip = tf->tf_rip; rbp = tf->tf_rbp; switch (frame_type) { case TRAP: db_printf("--- trap %#lr", tf->tf_trapno); break; case SYSCALL: db_printf("--- syscall"); decode_syscall(tf->tf_rax, td); break; case INTERRUPT: db_printf("--- interrupt"); break; default: panic("The moon has moved again."); } db_printf(", rip = %#lr, rsp = %#lr, rbp = %#lr ---\n", rip, rsp, rbp); } *ip = (db_addr_t) rip; *fp = (struct amd64_frame *) rbp; } static int db_backtrace(struct thread *td, struct trapframe *tf, struct amd64_frame *frame, db_addr_t pc, int count) { struct amd64_frame *actframe; #define MAXNARG 16 char *argnames[MAXNARG], **argnp = NULL; const char *name; long *argp; db_expr_t offset; c_db_sym_t sym; int narg; boolean_t first; if (count == -1) count = 1024; first = TRUE; while (count--) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); /* * Attempt to determine a (possibly fake) frame that gives * the caller's pc. It may differ from `frame' if the * current function never sets up a standard frame or hasn't * set one up yet or has just discarded one. The last two * cases can be guessed fairly reliably for code generated * by gcc. The first case is too much trouble to handle in * general because the amount of junk on the stack depends * on the pc (the special handling of "calltrap", etc. in * db_nextframe() works because the `next' pc is special). */ actframe = frame; if (first) { if (tf != NULL) { int instr; instr = db_get_value(pc, 4, FALSE); if ((instr & 0xffffffff) == 0xe5894855) { /* pushq %rbp; movq %rsp, %rbp */ actframe = (void *)(get_rsp(tf) - 8); } else if ((instr & 0xffffff) == 0xe58948) { /* movq %rsp, %rbp */ actframe = (void *)get_rsp(tf); if (tf->tf_rbp == 0) { /* Fake frame better. */ frame = actframe; } } else if ((instr & 0xff) == 0xc3) { /* ret */ actframe = (void *)(get_rsp(tf) - 8); } else if (offset == 0) { /* Probably an assembler symbol. */ actframe = (void *)(get_rsp(tf) - 8); } } else if (strcmp(name, "fork_trampoline") == 0) { /* * Don't try to walk back on a stack for a * process that hasn't actually been run yet. */ db_print_stack_entry(name, 0, 0, 0, pc); break; } first = FALSE; } argp = &actframe->f_arg0; narg = MAXNARG; if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) { argnp = argnames; } else { narg = db_numargs(frame); } db_print_stack_entry(name, narg, argnp, argp, pc); if (actframe != frame) { /* `frame' belongs to caller. */ pc = (db_addr_t) db_get_value((long)&actframe->f_retaddr, 8, FALSE); continue; } db_nextframe(&frame, &pc, td); if (INKERNEL((long)pc) && !INKERNEL((long)frame)) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); db_print_stack_entry(name, 0, 0, 0, pc); break; } if (!INKERNEL((long) frame)) { break; } } return (0); } -void -db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, - char *modif) -{ - struct thread *td; - - td = (have_addr) ? kdb_thr_lookup(addr) : kdb_thread; - if (td == NULL) { - db_printf("Thread %ld not found\n", addr); - return; - } - db_trace_thread(td, count); -} - void db_trace_self(void) { struct amd64_frame *frame; db_addr_t callpc; register_t rbp; __asm __volatile("movq %%rbp,%0" : "=r" (rbp)); frame = (struct amd64_frame *)rbp; callpc = (db_addr_t)db_get_value((long)&frame->f_retaddr, 8, FALSE); frame = frame->f_frame; db_backtrace(curthread, NULL, frame, callpc, -1); } int db_trace_thread(struct thread *thr, int count) { struct pcb *ctx; ctx = kdb_thr_ctx(thr); return (db_backtrace(thr, NULL, (struct amd64_frame *)ctx->pcb_rbp, ctx->pcb_rip, count)); } int amd64_set_watch(watchnum, watchaddr, size, access, d) int watchnum; unsigned int watchaddr; int size; int access; struct dbreg * d; { int i; unsigned int mask; if (watchnum == -1) { for (i = 0, mask = 0x3; i < 4; i++, mask <<= 2) if ((d->dr[7] & mask) == 0) break; if (i < 4) watchnum = i; else return (-1); } switch (access) { case DBREG_DR7_EXEC: size = 1; /* size must be 1 for an execution breakpoint */ /* fall through */ case DBREG_DR7_WRONLY: case DBREG_DR7_RDWR: break; default : return (-1); } /* * we can watch a 1, 2, or 4 byte sized location */ switch (size) { case 1 : mask = 0x00; break; case 2 : mask = 0x01 << 2; break; case 4 : mask = 0x03 << 2; break; default : return (-1); } mask |= access; /* clear the bits we are about to affect */ d->dr[7] &= ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16))); /* set drN register to the address, N=watchnum */ DBREG_DRX(d,watchnum) = watchaddr; /* enable the watchpoint */ d->dr[7] |= (0x2 << (watchnum*2)) | (mask << (watchnum*4+16)); return (watchnum); } int amd64_clr_watch(watchnum, d) int watchnum; struct dbreg * d; { if (watchnum < 0 || watchnum >= 4) return (-1); d->dr[7] = d->dr[7] & ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16))); DBREG_DRX(d,watchnum) = 0; return (0); } int db_md_set_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { int avail, wsize; int i; struct dbreg d; fill_dbregs(NULL, &d); avail = 0; for(i=0; i<4; i++) { if ((d.dr[7] & (3 << (i*2))) == 0) avail++; } if (avail*4 < size) return (-1); for (i=0; i<4 && (size != 0); i++) { if ((d.dr[7] & (3<<(i*2))) == 0) { if (size > 4) wsize = 4; else wsize = size; if (wsize == 3) wsize++; amd64_set_watch(i, addr, wsize, DBREG_DR7_WRONLY, &d); addr += wsize; size -= wsize; } } set_dbregs(NULL, &d); return(0); } int db_md_clr_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { int i; struct dbreg d; fill_dbregs(NULL, &d); for(i=0; i<4; i++) { if (d.dr[7] & (3 << (i*2))) { if ((DBREG_DRX((&d), i) >= addr) && (DBREG_DRX((&d), i) < addr+size)) amd64_clr_watch(i, &d); } } set_dbregs(NULL, &d); return(0); } static char * watchtype_str(type) int type; { switch (type) { case DBREG_DR7_EXEC : return "execute"; break; case DBREG_DR7_RDWR : return "read/write"; break; case DBREG_DR7_WRONLY : return "write"; break; default : return "invalid"; break; } } void db_md_list_watchpoints() { int i; struct dbreg d; fill_dbregs(NULL, &d); db_printf("\nhardware watchpoints:\n"); db_printf(" watch status type len address\n"); db_printf(" ----- -------- ---------- --- ----------\n"); for (i=0; i<4; i++) { if (d.dr[7] & (0x03 << (i*2))) { unsigned type, len; type = (d.dr[7] >> (16+(i*4))) & 3; len = (d.dr[7] >> (16+(i*4)+2)) & 3; db_printf(" %-5d %-8s %10s %3d 0x%016lx\n", i, "enabled", watchtype_str(type), len + 1, DBREG_DRX((&d), i)); } else { db_printf(" %-5d disabled\n", i); } } db_printf("\ndebug register values:\n"); for (i=0; i<8; i++) { db_printf(" dr%d 0x%016lx\n", i, DBREG_DRX((&d), i)); } db_printf("\n"); } diff --git a/sys/arm/arm/db_trace.c b/sys/arm/arm/db_trace.c index 8ada1ef5ae82..b05e760d448c 100644 --- a/sys/arm/arm/db_trace.c +++ b/sys/arm/arm/db_trace.c @@ -1,262 +1,263 @@ /* $NetBSD: db_trace.c,v 1.8 2003/01/17 22:28:48 thorpej Exp $ */ /* * Copyright (c) 2000, 2001 Ben Harris * Copyright (c) 1996 Scott K. Stevens * * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * 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 ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS 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 the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INKERNEL(va) (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS) int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); void db_md_list_watchpoints(void); /* * APCS stack frames are awkward beasts, so I don't think even trying to use * a structure to represent them is a good idea. * * Here's the diagram from the APCS. Increasing address is _up_ the page. * * save code pointer [fp] <- fp points to here * return link value [fp, #-4] * return sp value [fp, #-8] * return fp value [fp, #-12] * [saved v7 value] * [saved v6 value] * [saved v5 value] * [saved v4 value] * [saved v3 value] * [saved v2 value] * [saved v1 value] * [saved a4 value] * [saved a3 value] * [saved a2 value] * [saved a1 value] * * The save code pointer points twelve bytes beyond the start of the * code sequence (usually a single STM) that created the stack frame. * We have to disassemble it if we want to know which of the optional * fields are actually present. */ #define FR_SCP (0) #define FR_RLV (-1) #define FR_RSP (-2) #define FR_RFP (-3) -void +static void db_stack_trace_cmd(addr, have_addr, count, modif) db_expr_t addr; int have_addr; db_expr_t count; char *modif; { u_int32_t *frame, *lastframe; c_db_sym_t sym; db_expr_t pc; char c, *cp = modif; const char *name; db_expr_t value; db_expr_t offset; boolean_t kernel_only = TRUE; boolean_t trace_thread = FALSE; int scp_offset; if (kdb_frame == NULL && !have_addr) return; while ((c = *cp++) != 0) { if (c == 'u') kernel_only = FALSE; if (c == 't') trace_thread = TRUE; } if (!have_addr) frame = (u_int32_t *)(kdb_frame->tf_r11); else { if (trace_thread) { struct proc *p; struct thread *td; pid_t pid = (pid_t)addr; LIST_FOREACH(p, &allproc, p_list) { if (p->p_pid == pid) break; } if (p == NULL) { db_printf("not found\n"); return; } if (!(p->p_sflag & PS_INMEM)) { db_printf("swapped out\n"); return; } td = FIRST_THREAD_IN_PROC(p); frame = (u_int32_t *)(td->td_pcb->un_32.pcb32_r11); db_printf("at %p\n", frame); } else frame = (u_int32_t *)(addr); } lastframe = NULL; scp_offset = -(get_pc_str_offset() >> 2); while (count-- && frame != NULL) { db_addr_t scp; u_int32_t savecode; int r; u_int32_t *rp; const char *sep; /* * In theory, the SCP isn't guaranteed to be in the function * that generated the stack frame. We hope for the best. */ scp = frame[FR_SCP]; db_printsym(scp, DB_STGY_PROC); db_printf("\n\t"); pc = kdb_frame->tf_pc; sym = db_search_symbol(pc, DB_STGY_ANY, &offset); if (sym == C_DB_SYM_NULL) { value = 0; name = "(null)"; } else db_symbol_values(sym, &name, &value); db_printf("%s() at ", name); db_printsym(pc, DB_STGY_PROC); db_printf("\n"); #ifdef __PROG26 db_printf("scp=0x%08x rlv=0x%08x (", scp, frame[FR_RLV] & R15_PC); db_printsym(frame[FR_RLV] & R15_PC, DB_STGY_PROC); db_printf(")\n"); #else db_printf("scp=0x%08x rlv=0x%08x (", scp, frame[FR_RLV]); db_printsym(frame[FR_RLV], DB_STGY_PROC); db_printf(")\n"); #endif db_printf("\trsp=0x%08x rfp=0x%08x", frame[FR_RSP], frame[FR_RFP]); savecode = ((u_int32_t *)scp)[scp_offset]; if ((savecode & 0x0e100000) == 0x08000000) { /* Looks like an STM */ rp = frame - 4; sep = "\n\t"; for (r = 10; r >= 0; r--) { if (savecode & (1 << r)) { db_printf("%sr%d=0x%08x", sep, r, *rp--); sep = (frame - rp) % 4 == 2 ? "\n\t" : " "; } } } db_printf("\n"); /* * Switch to next frame up */ if (frame[FR_RFP] == 0) break; /* Top of stack */ lastframe = frame; frame = (u_int32_t *)(frame[FR_RFP]); if (INKERNEL((int)frame)) { /* staying in kernel */ if (frame <= lastframe) { db_printf("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) { db_printf("Bad user frame pointer: %p\n", frame); break; } } } } /* XXX stubs */ void db_md_list_watchpoints() { } int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) { return (0); } int db_md_set_watchpoint(db_expr_t addr, db_expr_t size) { return (0); } + int db_trace_thread(struct thread *thr, int count) { uint32_t addr; if (thr == curthread) addr = (uint32_t)__builtin_frame_address(0); else addr = thr->td_pcb->un_32.pcb32_r11; db_stack_trace_cmd(addr, 1, -1, NULL); return (0); } void db_trace_self(void) { db_trace_thread(curthread, -1); } diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c index 30740abc444c..03fd66294af7 100644 --- a/sys/ddb/db_command.c +++ b/sys/ddb/db_command.c @@ -1,625 +1,666 @@ /* * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * 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 ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS 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 the * rights to redistribute these changes. */ /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 */ /* * Command dispatcher. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Exported global variables */ boolean_t db_cmd_loop_done; db_addr_t db_dot; db_addr_t db_last_addr; db_addr_t db_prev; db_addr_t db_next; SET_DECLARE(db_cmd_set, struct command); SET_DECLARE(db_show_cmd_set, struct command); static db_cmdfcn_t db_fncall; static db_cmdfcn_t db_gdb; static db_cmdfcn_t db_kill; static db_cmdfcn_t db_reset; +static db_cmdfcn_t db_stack_trace; static db_cmdfcn_t db_watchdog; /* XXX this is actually forward-static. */ extern struct command db_show_cmds[]; /* * if 'ed' style: 'dot' is set at start of last item printed, * and '+' points to next line. * Otherwise: 'dot' points to next item, '..' points to last. */ static boolean_t db_ed_style = TRUE; /* * Utility routine - discard tokens through end-of-line. */ void db_skip_to_eol() { int t; do { t = db_read_token(); } while (t != tEOL); } /* * Results of command search. */ #define CMD_UNIQUE 0 #define CMD_FOUND 1 #define CMD_NONE 2 #define CMD_AMBIGUOUS 3 #define CMD_HELP 4 static void db_cmd_list(struct command *table, struct command **aux_tablep, struct command **aux_tablep_end); static int db_cmd_search(char *name, struct command *table, struct command **aux_tablep, struct command **aux_tablep_end, struct command **cmdp); static void db_command(struct command **last_cmdp, struct command *cmd_table, struct command **aux_cmd_tablep, struct command **aux_cmd_tablep_end); /* * Search for command prefix. */ static int db_cmd_search(name, table, aux_tablep, aux_tablep_end, cmdp) char * name; struct command *table; struct command **aux_tablep; struct command **aux_tablep_end; struct command **cmdp; /* out */ { struct command *cmd; struct command **aux_cmdp; int result = CMD_NONE; for (cmd = table; cmd->name != 0; cmd++) { register char *lp; register char *rp; register int c; lp = name; rp = cmd->name; while ((c = *lp) == *rp) { if (c == 0) { /* complete match */ *cmdp = cmd; return (CMD_UNIQUE); } lp++; rp++; } if (c == 0) { /* end of name, not end of command - partial match */ if (result == CMD_FOUND) { result = CMD_AMBIGUOUS; /* but keep looking for a full match - this lets us match single letters */ } else { *cmdp = cmd; result = CMD_FOUND; } } } if (result == CMD_NONE && aux_tablep != 0) /* XXX repeat too much code. */ for (aux_cmdp = aux_tablep; aux_cmdp < aux_tablep_end; aux_cmdp++) { register char *lp; register char *rp; register int c; lp = name; rp = (*aux_cmdp)->name; while ((c = *lp) == *rp) { if (c == 0) { /* complete match */ *cmdp = *aux_cmdp; return (CMD_UNIQUE); } lp++; rp++; } if (c == 0) { /* end of name, not end of command - partial match */ if (result == CMD_FOUND) { result = CMD_AMBIGUOUS; /* but keep looking for a full match - this lets us match single letters */ } else { *cmdp = *aux_cmdp; result = CMD_FOUND; } } } if (result == CMD_NONE) { /* check for 'help' */ if (name[0] == 'h' && name[1] == 'e' && name[2] == 'l' && name[3] == 'p') result = CMD_HELP; } return (result); } static void db_cmd_list(table, aux_tablep, aux_tablep_end) struct command *table; struct command **aux_tablep; struct command **aux_tablep_end; { register struct command *cmd; register struct command **aux_cmdp; for (cmd = table; cmd->name != 0; cmd++) { db_printf("%-12s", cmd->name); db_end_line(); } if (aux_tablep == 0) return; for (aux_cmdp = aux_tablep; aux_cmdp < aux_tablep_end; aux_cmdp++) { db_printf("%-12s", (*aux_cmdp)->name); db_end_line(); } } static void db_command(last_cmdp, cmd_table, aux_cmd_tablep, aux_cmd_tablep_end) struct command **last_cmdp; /* IN_OUT */ struct command *cmd_table; struct command **aux_cmd_tablep; struct command **aux_cmd_tablep_end; { struct command *cmd; int t; char modif[TOK_STRING_SIZE]; db_expr_t addr, count; boolean_t have_addr = FALSE; int result; t = db_read_token(); if (t == tEOL) { /* empty line repeats last command, at 'next' */ cmd = *last_cmdp; addr = (db_expr_t)db_next; have_addr = FALSE; count = 1; modif[0] = '\0'; } else if (t == tEXCL) { db_fncall((db_expr_t)0, (boolean_t)0, (db_expr_t)0, (char *)0); return; } else if (t != tIDENT) { db_printf("?\n"); db_flush_lex(); return; } else { /* * Search for command */ while (cmd_table) { result = db_cmd_search(db_tok_string, cmd_table, aux_cmd_tablep, aux_cmd_tablep_end, &cmd); switch (result) { case CMD_NONE: db_printf("No such command\n"); db_flush_lex(); return; case CMD_AMBIGUOUS: db_printf("Ambiguous\n"); db_flush_lex(); return; case CMD_HELP: db_cmd_list(cmd_table, aux_cmd_tablep, aux_cmd_tablep_end); db_flush_lex(); return; default: break; } if ((cmd_table = cmd->more) != 0) { /* XXX usually no more aux's. */ aux_cmd_tablep = 0; if (cmd_table == db_show_cmds) { aux_cmd_tablep = SET_BEGIN(db_show_cmd_set); aux_cmd_tablep_end = SET_LIMIT(db_show_cmd_set); } t = db_read_token(); if (t != tIDENT) { db_cmd_list(cmd_table, aux_cmd_tablep, aux_cmd_tablep_end); db_flush_lex(); return; } } } if ((cmd->flag & CS_OWN) == 0) { /* * Standard syntax: * command [/modifier] [addr] [,count] */ t = db_read_token(); if (t == tSLASH) { t = db_read_token(); if (t != tIDENT) { db_printf("Bad modifier\n"); db_flush_lex(); return; } db_strcpy(modif, db_tok_string); } else { db_unread_token(t); modif[0] = '\0'; } if (db_expression(&addr)) { db_dot = (db_addr_t) addr; db_last_addr = db_dot; have_addr = TRUE; } else { addr = (db_expr_t) db_dot; have_addr = FALSE; } t = db_read_token(); if (t == tCOMMA) { if (!db_expression(&count)) { db_printf("Count missing\n"); db_flush_lex(); return; } } else { db_unread_token(t); count = -1; } if ((cmd->flag & CS_MORE) == 0) { db_skip_to_eol(); } } } *last_cmdp = cmd; if (cmd != 0) { /* * Execute the command. */ (*cmd->fcn)(addr, have_addr, count, modif); db_setup_paging(NULL, NULL, -1); if (cmd->flag & CS_SET_DOT) { /* * If command changes dot, set dot to * previous address displayed (if 'ed' style). */ if (db_ed_style) { db_dot = db_prev; } else { db_dot = db_next; } } else { /* * If command does not change dot, * set 'next' location to be the same. */ db_next = db_dot; } } } /* * 'show' commands */ static struct command db_show_all_cmds[] = { { "procs", db_ps, 0, 0 }, { (char *)0 } }; static struct command db_show_cmds[] = { { "all", 0, 0, db_show_all_cmds }, { "registers", db_show_regs, 0, 0 }, { "breaks", db_listbreak_cmd, 0, 0 }, { "threads", db_show_threads, 0, 0 }, { (char *)0, } }; static struct command db_command_table[] = { { "print", db_print_cmd, 0, 0 }, { "p", db_print_cmd, 0, 0 }, { "examine", db_examine_cmd, CS_SET_DOT, 0 }, { "x", db_examine_cmd, CS_SET_DOT, 0 }, { "search", db_search_cmd, CS_OWN|CS_SET_DOT, 0 }, { "set", db_set_cmd, CS_OWN, 0 }, { "write", db_write_cmd, CS_MORE|CS_SET_DOT, 0 }, { "w", db_write_cmd, CS_MORE|CS_SET_DOT, 0 }, { "delete", db_delete_cmd, 0, 0 }, { "d", db_delete_cmd, 0, 0 }, { "break", db_breakpoint_cmd, 0, 0 }, { "dwatch", db_deletewatch_cmd, 0, 0 }, { "watch", db_watchpoint_cmd, CS_MORE,0 }, { "dhwatch", db_deletehwatch_cmd, 0, 0 }, { "hwatch", db_hwatchpoint_cmd, 0, 0 }, { "step", db_single_step_cmd, 0, 0 }, { "s", db_single_step_cmd, 0, 0 }, { "continue", db_continue_cmd, 0, 0 }, { "c", db_continue_cmd, 0, 0 }, { "until", db_trace_until_call_cmd,0, 0 }, { "next", db_trace_until_matching_cmd,0, 0 }, { "match", db_trace_until_matching_cmd,0, 0 }, - { "trace", db_stack_trace_cmd, 0, 0 }, - { "where", db_stack_trace_cmd, 0, 0 }, + { "trace", db_stack_trace, 0, 0 }, + { "where", db_stack_trace, 0, 0 }, { "call", db_fncall, CS_OWN, 0 }, { "show", 0, 0, db_show_cmds }, { "ps", db_ps, 0, 0 }, { "gdb", db_gdb, 0, 0 }, { "reset", db_reset, 0, 0 }, { "kill", db_kill, CS_OWN, 0 }, { "watchdog", db_watchdog, 0, 0 }, { "thread", db_set_thread, CS_OWN, 0 }, { (char *)0, } }; static struct command *db_last_command = 0; /* * At least one non-optional command must be implemented using * DB_COMMAND() so that db_cmd_set gets created. Here is one. */ DB_COMMAND(panic, db_panic) { panic("from debugger"); } void db_command_loop() { /* * Initialize 'prev' and 'next' to dot. */ db_prev = db_dot; db_next = db_dot; db_cmd_loop_done = 0; while (!db_cmd_loop_done) { if (db_print_position() != 0) db_printf("\n"); db_printf("db> "); (void) db_read_line(); db_command(&db_last_command, db_command_table, SET_BEGIN(db_cmd_set), SET_LIMIT(db_cmd_set)); } } void db_error(s) const char *s; { if (s) db_printf("%s", s); db_flush_lex(); kdb_reenter(); } /* * Call random function: * !expr(arg,arg,arg) */ static void db_fncall(dummy1, dummy2, dummy3, dummy4) db_expr_t dummy1; boolean_t dummy2; db_expr_t dummy3; char * dummy4; { db_expr_t fn_addr; #define MAXARGS 11 /* XXX only 10 are passed */ db_expr_t args[MAXARGS]; int nargs = 0; db_expr_t retval; typedef db_expr_t fcn_10args_t(db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t); fcn_10args_t *func; int t; if (!db_expression(&fn_addr)) { db_printf("Bad function\n"); db_flush_lex(); return; } func = (fcn_10args_t *)fn_addr; /* XXX */ t = db_read_token(); if (t == tLPAREN) { if (db_expression(&args[0])) { nargs++; while ((t = db_read_token()) == tCOMMA) { if (nargs == MAXARGS) { db_printf("Too many arguments\n"); db_flush_lex(); return; } if (!db_expression(&args[nargs])) { db_printf("Argument missing\n"); db_flush_lex(); return; } nargs++; } db_unread_token(t); } if (db_read_token() != tRPAREN) { db_printf("?\n"); db_flush_lex(); return; } } db_skip_to_eol(); while (nargs < MAXARGS) { args[nargs++] = 0; } retval = (*func)(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9] ); db_printf("%#lr\n", (long)retval); } static void db_kill(dummy1, dummy2, dummy3, dummy4) db_expr_t dummy1; boolean_t dummy2; db_expr_t dummy3; char * dummy4; { db_expr_t old_radix, pid, sig; struct proc *p; #define DB_ERROR(f) do { db_printf f; db_flush_lex(); goto out; } while (0) /* * PIDs and signal numbers are typically represented in base * 10, so make that the default here. It can, of course, be * overridden by specifying a prefix. */ old_radix = db_radix; db_radix = 10; /* Retrieve arguments. */ if (!db_expression(&sig)) DB_ERROR(("Missing signal number\n")); if (!db_expression(&pid)) DB_ERROR(("Missing process ID\n")); db_skip_to_eol(); if (sig < 0 || sig > _SIG_MAXSIG) DB_ERROR(("Signal number out of range\n")); /* * Find the process in question. allproc_lock is not needed * since we're in DDB. */ /* sx_slock(&allproc_lock); */ LIST_FOREACH(p, &allproc, p_list) if (p->p_pid == pid) break; /* sx_sunlock(&allproc_lock); */ if (p == NULL) DB_ERROR(("Can't find process with pid %ld\n", (long) pid)); /* If it's already locked, bail; otherwise, do the deed. */ if (PROC_TRYLOCK(p) == 0) DB_ERROR(("Can't lock process with pid %ld\n", (long) pid)); else { psignal(p, sig); PROC_UNLOCK(p); } out: db_radix = old_radix; #undef DB_ERROR } static void db_reset(dummy1, dummy2, dummy3, dummy4) db_expr_t dummy1; boolean_t dummy2; db_expr_t dummy3; char * dummy4; { cpu_reset(); } static void db_watchdog(dummy1, dummy2, dummy3, dummy4) db_expr_t dummy1; boolean_t dummy2; db_expr_t dummy3; char * dummy4; { int i; /* * XXX: It might make sense to be able to set the watchdog to a * XXX: timeout here so that failure or hang as a result of subsequent * XXX: ddb commands could be recovered by a reset. */ EVENTHANDLER_INVOKE(watchdog_list, 0, &i); } static void db_gdb(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3, char *dummy4) { if (kdb_dbbe_select("gdb") != 0) db_printf("The remote GDB backend could not be selected.\n"); else db_printf("Step to enter the remote GDB backend.\n"); } + +static void +db_stack_trace(db_expr_t tid, boolean_t hastid, db_expr_t count, char *modif) +{ + struct thread *td; + db_expr_t radix; + int t; + + /* + * We parse our own arguments. We don't like the default radix. + */ + radix = db_radix; + db_radix = 10; + hastid = db_expression(&tid); + t = db_read_token(); + if (t == tCOMMA) { + if (!db_expression(&count)) { + db_printf("Count missing\n"); + db_flush_lex(); + return; + } + } else { + db_unread_token(t); + count = -1; + } + db_skip_to_eol(); + db_radix = radix; + + if (hastid) { + td = kdb_thr_lookup((lwpid_t)tid); + if (td == NULL) + td = kdb_thr_from_pid((pid_t)tid); + if (td == NULL) { + db_printf("Thread %d not found\n", (int)tid); + return; + } + } else + td = kdb_thread; + db_trace_thread(td, count); +} diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h index c1ffff9c41d1..97d332e5bf39 100644 --- a/sys/ddb/ddb.h +++ b/sys/ddb/ddb.h @@ -1,153 +1,152 @@ /*- * Copyright (c) 1993, Garrett A. Wollman. * Copyright (c) 1993, University of Vermont and State Agricultural College. * 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. * 3. Neither the name of the University 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 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. * * $FreeBSD$ */ /* * Necessary declarations for the `ddb' kernel debugger. */ #ifndef _DDB_DDB_H_ #define _DDB_DDB_H_ #include /* type definitions */ #define DB_LINES_PER_PAGE 20 typedef void db_cmdfcn_t(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif); typedef void db_page_calloutfcn_t(void *arg); #define DB_COMMAND(cmd_name, func_name) \ DB_SET(cmd_name, func_name, db_cmd_set, 0, NULL) #define DB_SHOW_COMMAND(cmd_name, func_name) \ DB_SET(cmd_name, func_name, db_show_cmd_set, 0, NULL) #define DB_SET(cmd_name, func_name, set, flag, more) \ static db_cmdfcn_t func_name; \ \ static const struct command __CONCAT(func_name,_cmd) = { \ __STRING(cmd_name), \ func_name, \ flag, \ more \ }; \ TEXT_SET(set, __CONCAT(func_name,_cmd)); \ \ static void \ func_name(addr, have_addr, count, modif) \ db_expr_t addr; \ boolean_t have_addr; \ db_expr_t count; \ char *modif; extern db_expr_t db_maxoff; extern int db_indent; extern int db_inst_count; extern int db_load_count; extern int debugger_on_panic; extern int db_store_count; extern db_expr_t db_radix; extern db_expr_t db_max_width; extern db_expr_t db_tab_stop_width; struct thread; struct vm_map; void db_check_interrupt(void); void db_clear_watchpoints(void); db_addr_t db_disasm(db_addr_t loc, boolean_t altfmt); /* instruction disassembler */ void db_error(const char *s); int db_expression(db_expr_t *valuep); int db_get_variable(db_expr_t *valuep); void db_iprintf(const char *,...) __printflike(1, 2); struct vm_map *db_map_addr(vm_offset_t); boolean_t db_map_current(struct vm_map *); boolean_t db_map_equal(struct vm_map *, struct vm_map *); void db_print_loc_and_inst(db_addr_t loc); void db_print_thread(void); void db_printf(const char *fmt, ...) __printflike(1, 2); int db_read_bytes(vm_offset_t addr, size_t size, char *data); /* machine-dependent */ int db_readline(char *lstart, int lsize); void db_restart_at_pc(boolean_t watchpt); int db_set_variable(db_expr_t value); void db_set_watchpoints(void); void db_setup_paging(db_page_calloutfcn_t *callout, void *arg, int maxlines); void db_skip_to_eol(void); boolean_t db_stop_at_pc(boolean_t *is_breakpoint); #define db_strcpy strcpy void db_trace_self(void); int db_trace_thread(struct thread *, int); int db_value_of_name(const char *name, db_expr_t *valuep); int db_write_bytes(vm_offset_t addr, size_t size, char *data); db_cmdfcn_t db_breakpoint_cmd; db_cmdfcn_t db_continue_cmd; db_cmdfcn_t db_delete_cmd; db_cmdfcn_t db_deletehwatch_cmd; db_cmdfcn_t db_deletewatch_cmd; db_cmdfcn_t db_examine_cmd; db_cmdfcn_t db_hwatchpoint_cmd; db_cmdfcn_t db_listbreak_cmd; db_cmdfcn_t db_print_cmd; db_cmdfcn_t db_ps; db_cmdfcn_t db_search_cmd; db_cmdfcn_t db_set_cmd; db_cmdfcn_t db_set_thread; db_cmdfcn_t db_show_regs; db_cmdfcn_t db_show_threads; db_cmdfcn_t db_single_step_cmd; -db_cmdfcn_t db_stack_trace_cmd; db_cmdfcn_t db_trace_until_call_cmd; db_cmdfcn_t db_trace_until_matching_cmd; db_cmdfcn_t db_watchpoint_cmd; db_cmdfcn_t db_write_cmd; db_page_calloutfcn_t db_simple_pager; /* * Command table. */ struct command { char * name; /* command name */ db_cmdfcn_t *fcn; /* function to call */ int flag; /* extra info: */ #define CS_OWN 0x1 /* non-standard syntax */ #define CS_MORE 0x2 /* standard syntax, but may have other words * at end */ #define CS_SET_DOT 0x100 /* set dot after command */ struct command *more; /* another level of command */ }; #endif /* !_DDB_DDB_H_ */ diff --git a/sys/i386/i386/db_trace.c b/sys/i386/i386/db_trace.c index aaf11e0ce96b..d7ae3e3ffec2 100644 --- a/sys/i386/i386/db_trace.c +++ b/sys/i386/i386/db_trace.c @@ -1,673 +1,659 @@ /* * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * 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 ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS 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 the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_dr0; static db_varfcn_t db_dr1; static db_varfcn_t db_dr2; static db_varfcn_t db_dr3; static db_varfcn_t db_dr4; static db_varfcn_t db_dr5; static db_varfcn_t db_dr6; static db_varfcn_t db_dr7; static db_varfcn_t db_esp; static db_varfcn_t db_frame; static db_varfcn_t db_ss; /* * Machine register set. */ #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "cs", DB_OFFSET(tf_cs), db_frame }, { "ds", DB_OFFSET(tf_ds), db_frame }, { "es", DB_OFFSET(tf_es), db_frame }, { "fs", DB_OFFSET(tf_fs), db_frame }, { "ss", NULL, db_ss }, { "eax", DB_OFFSET(tf_eax), db_frame }, { "ecx", DB_OFFSET(tf_ecx), db_frame }, { "edx", DB_OFFSET(tf_edx), db_frame }, { "ebx", DB_OFFSET(tf_ebx), db_frame }, { "esp", NULL, db_esp }, { "ebp", DB_OFFSET(tf_ebp), db_frame }, { "esi", DB_OFFSET(tf_esi), db_frame }, { "edi", DB_OFFSET(tf_edi), db_frame }, { "eip", DB_OFFSET(tf_eip), db_frame }, { "efl", DB_OFFSET(tf_eflags), db_frame }, { "dr0", NULL, db_dr0 }, { "dr1", NULL, db_dr1 }, { "dr2", NULL, db_dr2 }, { "dr3", NULL, db_dr3 }, { "dr4", NULL, db_dr4 }, { "dr5", NULL, db_dr5 }, { "dr6", NULL, db_dr6 }, { "dr7", NULL, db_dr7 }, }; struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); #define DB_DRX_FUNC(reg) \ static int \ db_ ## reg (vp, valuep, op) \ struct db_variable *vp; \ db_expr_t * valuep; \ int op; \ { \ if (op == DB_VAR_GET) \ *valuep = r ## reg (); \ else \ load_ ## reg (*valuep); \ return (1); \ } DB_DRX_FUNC(dr0) DB_DRX_FUNC(dr1) DB_DRX_FUNC(dr2) DB_DRX_FUNC(dr3) DB_DRX_FUNC(dr4) DB_DRX_FUNC(dr5) DB_DRX_FUNC(dr6) DB_DRX_FUNC(dr7) static __inline int get_esp(struct trapframe *tf) { return ((ISPL(tf->tf_cs)) ? tf->tf_esp : (db_expr_t)tf + (uintptr_t)DB_OFFSET(tf_esp)); } static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { int *reg; if (kdb_frame == NULL) return (0); reg = (int *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } static int db_esp(struct db_variable *vp, db_expr_t *valuep, int op) { if (kdb_frame == NULL) return (0); if (op == DB_VAR_GET) *valuep = get_esp(kdb_frame); else if (ISPL(kdb_frame->tf_cs)) kdb_frame->tf_esp = *valuep; return (1); } static int db_ss(struct db_variable *vp, db_expr_t *valuep, int op) { if (kdb_frame == NULL) return (0); if (op == DB_VAR_GET) *valuep = (ISPL(kdb_frame->tf_cs)) ? kdb_frame->tf_ss : rss(); else if (ISPL(kdb_frame->tf_cs)) kdb_frame->tf_ss = *valuep; return (1); } /* * Stack trace. */ #define INKERNEL(va) (((vm_offset_t)(va)) >= USRSTACK) struct i386_frame { struct i386_frame *f_frame; int f_retaddr; int f_arg0; }; #define NORMAL 0 #define TRAP 1 #define INTERRUPT 2 #define SYSCALL 3 static void db_nextframe(struct i386_frame **, db_addr_t *, struct thread *); static int db_numargs(struct i386_frame *); static void db_print_stack_entry(const char *, int, char **, int *, db_addr_t); static void decode_syscall(int, struct thread *); static char * watchtype_str(int type); int i386_set_watch(int watchnum, unsigned int watchaddr, int size, int access, struct dbreg * d); int i386_clr_watch(int watchnum, struct dbreg * d); int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); void db_md_list_watchpoints(void); /* * Figure out how many arguments were passed into the frame at "fp". */ static int db_numargs(fp) struct i386_frame *fp; { int *argp; int inst; int args; argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE); /* * XXX etext is wrong for LKMs. We should attempt to interpret * the instruction at the return address in all cases. This * may require better fault handling. */ if (argp < (int *)btext || argp >= (int *)etext) { args = 5; } else { inst = db_get_value((int)argp, 4, FALSE); if ((inst & 0xff) == 0x59) /* popl %ecx */ args = 1; else if ((inst & 0xffff) == 0xc483) /* addl $Ibs, %esp */ args = ((inst >> 16) & 0xff) / 4; else args = 5; } return (args); } static void db_print_stack_entry(name, narg, argnp, argp, callpc) const char *name; int narg; char **argnp; int *argp; db_addr_t callpc; { db_printf("%s(", name); while (narg) { if (argnp) db_printf("%s=", *argnp++); db_printf("%r", db_get_value((int)argp, 4, FALSE)); argp++; if (--narg != 0) db_printf(","); } db_printf(") at "); db_printsym(callpc, DB_STGY_PROC); db_printf("\n"); } static void decode_syscall(int number, struct thread *td) { struct proc *p; c_db_sym_t sym; db_expr_t diff; sy_call_t *f; const char *symname; db_printf(" (%d", number); p = (td != NULL) ? td->td_proc : NULL; if (p != NULL && 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) { db_symbol_values(sym, &symname, NULL); db_printf(", %s, %s", p->p_sysent->sv_name, symname); } } db_printf(")"); } /* * Figure out the next frame up in the call stack. */ static void db_nextframe(struct i386_frame **fp, db_addr_t *ip, struct thread *td) { struct trapframe *tf; int frame_type; int eip, esp, ebp; db_expr_t offset; c_db_sym_t sym; const char *name; eip = db_get_value((int) &(*fp)->f_retaddr, 4, FALSE); ebp = db_get_value((int) &(*fp)->f_frame, 4, FALSE); /* * Figure out frame type. */ frame_type = NORMAL; sym = db_search_symbol(eip, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); if (name != NULL) { if (strcmp(name, "calltrap") == 0 || strcmp(name, "fork_trampoline") == 0) frame_type = TRAP; else if (strncmp(name, "Xatpic_intr", 11) == 0 || strncmp(name, "Xapic_isr", 9) == 0) frame_type = INTERRUPT; else if (strcmp(name, "Xlcall_syscall") == 0 || strcmp(name, "Xint0x80_syscall") == 0) frame_type = SYSCALL; } /* * Normal frames need no special processing. */ if (frame_type == NORMAL) { *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; return; } db_print_stack_entry(name, 0, 0, 0, eip); /* * Point to base of trapframe which is just above the * current frame. */ if (frame_type == INTERRUPT) tf = (struct trapframe *)((int)*fp + 12); else tf = (struct trapframe *)((int)*fp + 8); if (INKERNEL((int) tf)) { esp = get_esp(tf); eip = tf->tf_eip; ebp = tf->tf_ebp; switch (frame_type) { case TRAP: db_printf("--- trap %#r", tf->tf_trapno); break; case SYSCALL: db_printf("--- syscall"); decode_syscall(tf->tf_eax, td); break; case INTERRUPT: db_printf("--- interrupt"); break; default: panic("The moon has moved again."); } db_printf(", eip = %#r, esp = %#r, ebp = %#r ---\n", eip, esp, ebp); } *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; } static int db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, db_addr_t pc, int count) { struct i386_frame *actframe; #define MAXNARG 16 char *argnames[MAXNARG], **argnp = NULL; const char *name; int *argp; db_expr_t offset; c_db_sym_t sym; int narg; boolean_t first; if (count == -1) count = 1024; first = TRUE; while (count--) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); /* * Attempt to determine a (possibly fake) frame that gives * the caller's pc. It may differ from `frame' if the * current function never sets up a standard frame or hasn't * set one up yet or has just discarded one. The last two * cases can be guessed fairly reliably for code generated * by gcc. The first case is too much trouble to handle in * general because the amount of junk on the stack depends * on the pc (the special handling of "calltrap", etc. in * db_nextframe() works because the `next' pc is special). */ actframe = frame; if (first) { if (tf != NULL) { int instr; instr = db_get_value(pc, 4, FALSE); if ((instr & 0xffffff) == 0x00e58955) { /* pushl %ebp; movl %esp, %ebp */ actframe = (void *)(get_esp(tf) - 4); } else if ((instr & 0xffff) == 0x0000e589) { /* movl %esp, %ebp */ actframe = (void *)get_esp(tf); if (tf->tf_ebp == 0) { /* Fake frame better. */ frame = actframe; } } else if ((instr & 0xff) == 0x000000c3) { /* ret */ actframe = (void *)(get_esp(tf) - 4); } else if (offset == 0) { /* Probably an assembler symbol. */ actframe = (void *)(get_esp(tf) - 4); } } else if (strcmp(name, "fork_trampoline") == 0) { /* * Don't try to walk back on a stack for a * process that hasn't actually been run yet. */ db_print_stack_entry(name, 0, 0, 0, pc); break; } first = FALSE; } argp = &actframe->f_arg0; narg = MAXNARG; if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) { argnp = argnames; } else { narg = db_numargs(frame); } db_print_stack_entry(name, narg, argnp, argp, pc); if (actframe != frame) { /* `frame' belongs to caller. */ pc = (db_addr_t) db_get_value((int)&actframe->f_retaddr, 4, FALSE); continue; } db_nextframe(&frame, &pc, td); if (INKERNEL((int)pc) && !INKERNEL((int) frame)) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); db_print_stack_entry(name, 0, 0, 0, pc); break; } if (!INKERNEL((int) frame)) { break; } } return (0); } -void -db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, - char *modif) -{ - struct thread *td; - - td = (have_addr) ? kdb_thr_lookup(addr) : kdb_thread; - if (td == NULL) { - db_printf("Thread %d not found\n", addr); - return; - } - db_trace_thread(td, count); -} - void db_trace_self(void) { struct i386_frame *frame; db_addr_t callpc; register_t ebp; __asm __volatile("movl %%ebp,%0" : "=r" (ebp)); frame = (struct i386_frame *)ebp; callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE); frame = frame->f_frame; db_backtrace(curthread, NULL, frame, callpc, -1); } int db_trace_thread(struct thread *thr, int count) { struct pcb *ctx; ctx = kdb_thr_ctx(thr); return (db_backtrace(thr, NULL, (struct i386_frame *)ctx->pcb_ebp, ctx->pcb_eip, count)); } int i386_set_watch(watchnum, watchaddr, size, access, d) int watchnum; unsigned int watchaddr; int size; int access; struct dbreg * d; { int i; unsigned int mask; if (watchnum == -1) { for (i = 0, mask = 0x3; i < 4; i++, mask <<= 2) if ((d->dr[7] & mask) == 0) break; if (i < 4) watchnum = i; else return (-1); } switch (access) { case DBREG_DR7_EXEC: size = 1; /* size must be 1 for an execution breakpoint */ /* fall through */ case DBREG_DR7_WRONLY: case DBREG_DR7_RDWR: break; default : return (-1); } /* * we can watch a 1, 2, or 4 byte sized location */ switch (size) { case 1 : mask = 0x00; break; case 2 : mask = 0x01 << 2; break; case 4 : mask = 0x03 << 2; break; default : return (-1); } mask |= access; /* clear the bits we are about to affect */ d->dr[7] &= ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16))); /* set drN register to the address, N=watchnum */ DBREG_DRX(d,watchnum) = watchaddr; /* enable the watchpoint */ d->dr[7] |= (0x2 << (watchnum*2)) | (mask << (watchnum*4+16)); return (watchnum); } int i386_clr_watch(watchnum, d) int watchnum; struct dbreg * d; { if (watchnum < 0 || watchnum >= 4) return (-1); d->dr[7] = d->dr[7] & ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16))); DBREG_DRX(d,watchnum) = 0; return (0); } int db_md_set_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { int avail, wsize; int i; struct dbreg d; fill_dbregs(NULL, &d); avail = 0; for(i=0; i<4; i++) { if ((d.dr[7] & (3 << (i*2))) == 0) avail++; } if (avail*4 < size) return (-1); for (i=0; i<4 && (size != 0); i++) { if ((d.dr[7] & (3<<(i*2))) == 0) { if (size > 4) wsize = 4; else wsize = size; if (wsize == 3) wsize++; i386_set_watch(i, addr, wsize, DBREG_DR7_WRONLY, &d); addr += wsize; size -= wsize; } } set_dbregs(NULL, &d); return(0); } int db_md_clr_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { int i; struct dbreg d; fill_dbregs(NULL, &d); for(i=0; i<4; i++) { if (d.dr[7] & (3 << (i*2))) { if ((DBREG_DRX((&d), i) >= addr) && (DBREG_DRX((&d), i) < addr+size)) i386_clr_watch(i, &d); } } set_dbregs(NULL, &d); return(0); } static char * watchtype_str(type) int type; { switch (type) { case DBREG_DR7_EXEC : return "execute"; break; case DBREG_DR7_RDWR : return "read/write"; break; case DBREG_DR7_WRONLY : return "write"; break; default : return "invalid"; break; } } void db_md_list_watchpoints() { int i; struct dbreg d; fill_dbregs(NULL, &d); db_printf("\nhardware watchpoints:\n"); db_printf(" watch status type len address\n"); db_printf(" ----- -------- ---------- --- ----------\n"); for (i=0; i<4; i++) { if (d.dr[7] & (0x03 << (i*2))) { unsigned type, len; type = (d.dr[7] >> (16+(i*4))) & 3; len = (d.dr[7] >> (16+(i*4)+2)) & 3; db_printf(" %-5d %-8s %10s %3d 0x%08x\n", i, "enabled", watchtype_str(type), len+1, DBREG_DRX((&d),i)); } else { db_printf(" %-5d disabled\n", i); } } db_printf("\ndebug register values:\n"); for (i=0; i<8; i++) { db_printf(" dr%d 0x%08x\n", i, DBREG_DRX((&d),i)); } db_printf("\n"); } diff --git a/sys/ia64/ia64/db_trace.c b/sys/ia64/ia64/db_trace.c index 11a2e5582771..7f9c53cd9614 100644 --- a/sys/ia64/ia64/db_trace.c +++ b/sys/ia64/ia64/db_trace.c @@ -1,181 +1,167 @@ /*- * Copyright (c) 2003, 2004 Marcel Moolenaar * Copyright (c) 2000-2001 Doug Rabson * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); void db_md_list_watchpoints(void); static int db_backtrace(struct thread *td, struct pcb *pcb, int count) { struct unw_regstate rs; struct trapframe *tf; const char *name; db_expr_t offset; uint64_t bsp, cfm, ip, pfs, reg, sp; c_db_sym_t sym; int args, error, i; error = unw_create_from_pcb(&rs, pcb); while (!error && count--) { error = unw_get_cfm(&rs, &cfm); if (!error) error = unw_get_bsp(&rs, &bsp); if (!error) error = unw_get_ip(&rs, &ip); if (!error) error = unw_get_sp(&rs, &sp); if (error) break; args = IA64_CFM_SOL(cfm); if (args > 8) args = 8; error = unw_step(&rs); if (!error) { if (!unw_get_cfm(&rs, &pfs)) { i = IA64_CFM_SOF(pfs) - IA64_CFM_SOL(pfs); if (args > i) args = i; } } sym = db_search_symbol(ip, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); db_printf("%s(", name); if (bsp >= IA64_RR_BASE(5)) { for (i = 0; i < args; i++) { if ((bsp & 0x1ff) == 0x1f8) bsp += 8; db_read_bytes(bsp, sizeof(reg), (void*)®); if (i > 0) db_printf(", "); db_printf("0x%lx", reg); bsp += 8; } } else db_printf("..."); db_printf(") at "); db_printsym(ip, DB_STGY_PROC); db_printf("\n"); if (error != EOVERFLOW) continue; if (sp < IA64_RR_BASE(5)) break; tf = (struct trapframe *)(sp + 16); if ((tf->tf_flags & FRAME_SYSCALL) != 0 || tf->tf_special.iip < IA64_RR_BASE(5)) break; /* XXX ask if we should unwind across the trapframe. */ db_printf("--- trapframe at %p\n", tf); unw_delete(&rs); error = unw_create_from_frame(&rs, tf); } unw_delete(&rs); return (error); } -void -db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, - char *modif) -{ - struct thread *td; - - td = (have_addr) ? kdb_thr_lookup(addr) : kdb_thread; - if (td == NULL) { - db_printf("Thread %d not found\n", (int)addr); - return; - } - db_trace_thread(td, count); -} - void db_trace_self(void) { struct pcb pcb; savectx(&pcb); db_backtrace(curthread, &pcb, -1); } int db_trace_thread(struct thread *td, int count) { struct pcb *ctx; ctx = kdb_thr_ctx(td); return (db_backtrace(td, ctx, count)); } int db_md_set_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { return (-1); } int db_md_clr_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { return (-1); } void db_md_list_watchpoints() { return; } diff --git a/sys/powerpc/powerpc/db_trace.c b/sys/powerpc/powerpc/db_trace.c index a4291d95c469..9fe1cc38322a 100644 --- a/sys/powerpc/powerpc/db_trace.c +++ b/sys/powerpc/powerpc/db_trace.c @@ -1,300 +1,286 @@ /* $FreeBSD$ */ /* $NetBSD: db_trace.c,v 1.20 2002/05/13 20:30:09 matt Exp $ */ /* $OpenBSD: db_trace.c,v 1.3 1997/03/21 02:10:48 niklas Exp $ */ /* * Mach Operating System * Copyright (c) 1992 Carnegie Mellon University * 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 ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_frame; #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "r0", DB_OFFSET(fixreg[0]), db_frame }, { "r1", DB_OFFSET(fixreg[1]), db_frame }, { "r2", DB_OFFSET(fixreg[2]), db_frame }, { "r3", DB_OFFSET(fixreg[3]), db_frame }, { "r4", DB_OFFSET(fixreg[4]), db_frame }, { "r5", DB_OFFSET(fixreg[5]), db_frame }, { "r6", DB_OFFSET(fixreg[6]), db_frame }, { "r7", DB_OFFSET(fixreg[7]), db_frame }, { "r8", DB_OFFSET(fixreg[8]), db_frame }, { "r9", DB_OFFSET(fixreg[9]), db_frame }, { "r10", DB_OFFSET(fixreg[10]), db_frame }, { "r11", DB_OFFSET(fixreg[11]), db_frame }, { "r12", DB_OFFSET(fixreg[12]), db_frame }, { "r13", DB_OFFSET(fixreg[13]), db_frame }, { "r14", DB_OFFSET(fixreg[14]), db_frame }, { "r15", DB_OFFSET(fixreg[15]), db_frame }, { "r16", DB_OFFSET(fixreg[16]), db_frame }, { "r17", DB_OFFSET(fixreg[17]), db_frame }, { "r18", DB_OFFSET(fixreg[18]), db_frame }, { "r19", DB_OFFSET(fixreg[19]), db_frame }, { "r20", DB_OFFSET(fixreg[20]), db_frame }, { "r21", DB_OFFSET(fixreg[21]), db_frame }, { "r22", DB_OFFSET(fixreg[22]), db_frame }, { "r23", DB_OFFSET(fixreg[23]), db_frame }, { "r24", DB_OFFSET(fixreg[24]), db_frame }, { "r25", DB_OFFSET(fixreg[25]), db_frame }, { "r26", DB_OFFSET(fixreg[26]), db_frame }, { "r27", DB_OFFSET(fixreg[27]), db_frame }, { "r28", DB_OFFSET(fixreg[28]), db_frame }, { "r29", DB_OFFSET(fixreg[29]), db_frame }, { "r30", DB_OFFSET(fixreg[30]), db_frame }, { "r31", DB_OFFSET(fixreg[31]), db_frame }, { "srr0", DB_OFFSET(srr0), db_frame }, { "srr1", DB_OFFSET(srr1), db_frame }, { "lr", DB_OFFSET(lr), db_frame }, { "ctr", DB_OFFSET(ctr), db_frame }, { "cr", DB_OFFSET(cr), db_frame }, { "xer", DB_OFFSET(xer), db_frame }, { "dar", DB_OFFSET(dar), db_frame }, { "dsisr", DB_OFFSET(dsisr), db_frame }, }; struct db_variable *db_eregs = db_regs + sizeof (db_regs)/sizeof (db_regs[0]); extern int trapexit[]; extern int end[]; /* * register variable handling */ static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { uint32_t *reg; if (kdb_frame == NULL) return (0); reg = (uint32_t*)((uintptr_t)kdb_frame + (uintptr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } /* * Frame tracing. */ static int db_backtrace(struct thread *td, db_addr_t fp, int count) { db_addr_t stackframe, lr, *args; db_expr_t diff; c_db_sym_t sym; const char *symname; boolean_t kernel_only = TRUE; boolean_t full = FALSE; #if 0 { register char *cp = modif; register char c; while ((c = *cp++) != 0) { if (c == 't') trace_thread = TRUE; if (c == 'u') kernel_only = FALSE; if (c == 'f') full = TRUE; } } #endif stackframe = fp; for (;;) { if (stackframe < PAGE_SIZE) break; /* * Locate the next frame by grabbing the backchain ptr * from frame[0] */ stackframe = *(db_addr_t *)stackframe; next_frame: /* The saved arg values start at frame[2] */ args = (db_addr_t *)(stackframe + 8); if (stackframe < PAGE_SIZE) break; if (count-- == 0) break; /* * Extract link register from frame and subtract * 4 to convert into calling address (as opposed to * return address) */ lr = *(db_addr_t *)(stackframe + 4) - 4; if ((lr & 3) || (lr < 0x100)) { db_printf("saved LR(0x%x) is invalid.", lr); break; } db_printf("0x%08x: ", stackframe); /* * The trap code labels the return address from the * call to C code as 'trapexit'. Use this to determine * if the callframe has to traverse a saved trap context */ if (lr + 4 == (db_addr_t) &trapexit) { const char *trapstr; struct trapframe *tf = (struct trapframe *) (stackframe+8); db_printf("%s ", tf->srr1 & PSL_PR ? "user" : "kernel"); switch (tf->exc) { case EXC_DSI: db_printf("DSI %s trap @ %#x by ", tf->dsisr & DSISR_STORE ? "write" : "read", tf->dar); goto print_trap; case EXC_ALI: db_printf("ALI trap @ %#x (DSISR %#x) ", tf->dar, tf->dsisr); goto print_trap; case EXC_ISI: trapstr = "ISI"; break; case EXC_PGM: trapstr = "PGM"; break; case EXC_SC: trapstr = "SC"; break; case EXC_EXI: trapstr = "EXI"; break; case EXC_MCHK: trapstr = "MCHK"; break; case EXC_VEC: trapstr = "VEC"; break; case EXC_FPU: trapstr = "FPU"; break; case EXC_FPA: trapstr = "FPA"; break; case EXC_DECR: trapstr = "DECR"; break; case EXC_BPT: trapstr = "BPT"; break; case EXC_TRC: trapstr = "TRC"; break; case EXC_RUNMODETRC: trapstr = "RUNMODETRC"; break; case EXC_PERF: trapstr = "PERF"; break; case EXC_SMI: trapstr = "SMI"; break; case EXC_RST: trapstr = "RST"; break; default: trapstr = NULL; break; } if (trapstr != NULL) { db_printf("%s trap by ", trapstr); } else { db_printf("trap %#x by ", tf->exc); } print_trap: lr = (db_addr_t) tf->srr0; diff = 0; symname = NULL; sym = db_search_symbol(lr, DB_STGY_ANY, &diff); db_symbol_values(sym, &symname, 0); if (symname == NULL || !strcmp(symname, "end")) { db_printf("%#x: srr1=%#x\n", lr, tf->srr1); } else { db_printf("%s+%#x: srr1=%#x\n", symname, diff, tf->srr1); } db_printf("%-10s r1=%#x cr=%#x xer=%#x ctr=%#x", "", tf->fixreg[1], tf->cr, tf->xer, tf->ctr); if (tf->exc == EXC_DSI) db_printf(" dsisr=%#x", tf->dsisr); db_printf("\n"); stackframe = (db_addr_t) tf->fixreg[1]; if (kernel_only && (tf->srr1 & PSL_PR)) break; goto next_frame; } diff = 0; symname = NULL; sym = db_search_symbol(lr, DB_STGY_ANY, &diff); db_symbol_values(sym, &symname, 0); if (symname == NULL || !strcmp(symname, "end")) db_printf("at %x", lr); else db_printf("at %s+%#x", symname, diff); if (full) /* Print all the args stored in that stackframe. */ db_printf("(%x, %x, %x, %x, %x, %x, %x, %x)", args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); db_printf("\n"); } return (0); } -void -db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, - char *modif) -{ - struct thread *td; - - td = (have_addr) ? kdb_thr_lookup(addr) : kdb_thread; - if (td == NULL) { - db_printf("Thread %d not found\n", (int)addr); - return; - } - db_trace_thread(td, count); -} - void db_trace_self(void) { db_addr_t addr; addr = (db_addr_t)__builtin_frame_address(1); db_backtrace(curthread, addr, -1); } int db_trace_thread(struct thread *td, int count) { struct pcb *ctx; ctx = kdb_thr_ctx(td); return (db_backtrace(td, (db_addr_t)ctx->pcb_sp, count)); } diff --git a/sys/sparc64/sparc64/db_trace.c b/sys/sparc64/sparc64/db_trace.c index ae538413cc92..b5fdec9f42b0 100644 --- a/sys/sparc64/sparc64/db_trace.c +++ b/sys/sparc64/sparc64/db_trace.c @@ -1,304 +1,290 @@ /*- * Copyright (c) 2001 Jake Burkholder. * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INKERNEL(va) \ ((va) >= VM_MIN_KERNEL_ADDRESS && (va) <= VM_MAX_KERNEL_ADDRESS) static db_varfcn_t db_frame; #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "g0", DB_OFFSET(tf_global[0]), db_frame }, { "g1", DB_OFFSET(tf_global[1]), db_frame }, { "g2", DB_OFFSET(tf_global[2]), db_frame }, { "g3", DB_OFFSET(tf_global[3]), db_frame }, { "g4", DB_OFFSET(tf_global[4]), db_frame }, { "g5", DB_OFFSET(tf_global[5]), db_frame }, { "g6", DB_OFFSET(tf_global[6]), db_frame }, { "g7", DB_OFFSET(tf_global[7]), db_frame }, { "i0", DB_OFFSET(tf_out[0]), db_frame }, { "i1", DB_OFFSET(tf_out[1]), db_frame }, { "i2", DB_OFFSET(tf_out[2]), db_frame }, { "i3", DB_OFFSET(tf_out[3]), db_frame }, { "i4", DB_OFFSET(tf_out[4]), db_frame }, { "i5", DB_OFFSET(tf_out[5]), db_frame }, { "i6", DB_OFFSET(tf_out[6]), db_frame }, { "i7", DB_OFFSET(tf_out[7]), db_frame }, { "tnpc", DB_OFFSET(tf_tnpc), db_frame }, { "tpc", DB_OFFSET(tf_tpc), db_frame }, { "tstate", DB_OFFSET(tf_tstate), db_frame }, }; struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { uint64_t *reg; if (kdb_frame == NULL) return (0); reg = (uint64_t*)((uintptr_t)kdb_frame + (uintptr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } /* * User stack trace (debugging aid). */ static void db_utrace(struct thread *td, struct trapframe *tf) { struct pcb *pcb; db_addr_t sp, rsp, o7, pc; int i, found; pcb = td->td_pcb; sp = db_get_value((db_addr_t)&tf->tf_sp, sizeof(tf->tf_sp), FALSE); o7 = db_get_value((db_addr_t)&tf->tf_out[7], sizeof(tf->tf_out[7]), FALSE); pc = db_get_value((db_addr_t)&tf->tf_tpc, sizeof(tf->tf_tpc), FALSE); db_printf("user trace: trap %%o7=%#lx\n", o7); while (sp != 0) { db_printf("pc %#lx, sp %#lx\n", pc, sp); /* First, check whether the frame is in the pcb. */ found = 0; for (i = 0; i < pcb->pcb_nsaved; i++) { if (pcb->pcb_rwsp[i] == sp) { found = 1; sp = pcb->pcb_rw[i].rw_in[6]; pc = pcb->pcb_rw[i].rw_in[7]; break; } } if (!found) { rsp = sp + SPOFF; sp = 0; if (copyin((void *)(rsp + offsetof(struct frame, fr_fp)), &sp, sizeof(sp)) != 0 || copyin((void *)(rsp + offsetof(struct frame, fr_pc)), &pc, sizeof(pc)) != 0) break; } } db_printf("done\n"); } static int db_print_trap(struct thread *td, struct trapframe *tf) { struct proc *p; const char *symname; c_db_sym_t sym; db_expr_t diff; db_addr_t func; db_addr_t tpc; u_long type; u_long sfar; u_long sfsr; u_long tar; u_long level; u_long pil; u_long code; u_long o7; int user; p = td->td_proc; type = db_get_value((db_addr_t)&tf->tf_type, sizeof(tf->tf_type), FALSE); db_printf("-- %s", trap_msg[type & ~T_KERNEL]); switch (type & ~T_KERNEL) { case T_DATA_PROTECTION: tar = (u_long)db_get_value((db_addr_t)&tf->tf_tar, sizeof(tf->tf_tar), FALSE); db_printf(" tar=%#lx", tar); /* fall through */ case T_DATA_EXCEPTION: case T_INSTRUCTION_EXCEPTION: case T_MEM_ADDRESS_NOT_ALIGNED: sfar = (u_long)db_get_value((db_addr_t)&tf->tf_sfar, sizeof(tf->tf_sfar), FALSE); sfsr = (u_long)db_get_value((db_addr_t)&tf->tf_sfsr, sizeof(tf->tf_sfsr), FALSE); db_printf(" sfar=%#lx sfsr=%#lx", sfar, sfsr); break; case T_DATA_MISS: case T_INSTRUCTION_MISS: tar = (u_long)db_get_value((db_addr_t)&tf->tf_tar, sizeof(tf->tf_tar), FALSE); db_printf(" tar=%#lx", tar); break; case T_SYSCALL: code = db_get_value((db_addr_t)&tf->tf_global[1], sizeof(tf->tf_global[1]), FALSE); db_printf(" (%ld", code); if (code >= 0 && code < p->p_sysent->sv_size) { func = (db_addr_t)p->p_sysent->sv_table[code].sy_call; sym = db_search_symbol(func, DB_STGY_ANY, &diff); if (sym != DB_SYM_NULL && diff == 0) { db_symbol_values(sym, &symname, NULL); db_printf(", %s, %s", p->p_sysent->sv_name, symname); } db_printf(")"); } break; case T_INTERRUPT: level = (u_long)db_get_value((db_addr_t)&tf->tf_level, sizeof(tf->tf_level), FALSE); pil = (u_long)db_get_value((db_addr_t)&tf->tf_pil, sizeof(tf->tf_pil), FALSE); db_printf(" level=%#lx pil=%#lx", level, pil); break; default: break; } o7 = (u_long)db_get_value((db_addr_t)&tf->tf_out[7], sizeof(tf->tf_out[7]), FALSE); db_printf(" %%o7=%#lx --\n", o7); user = (type & T_KERNEL) == 0; if (user) { tpc = db_get_value((db_addr_t)&tf->tf_tpc, sizeof(tf->tf_tpc), FALSE); db_printf("userland() at "); db_printsym(tpc, DB_STGY_PROC); db_printf("\n"); db_utrace(td, tf); } return (user); } static int db_backtrace(struct thread *td, struct frame *fp, int count) { struct trapframe *tf; const char *name; c_db_sym_t sym; db_expr_t offset; db_expr_t value; db_addr_t npc; db_addr_t pc; int trap; int user; if (count == -1) count = 1024; trap = 0; user = 0; npc = 0; while (count-- && !user) { pc = (db_addr_t)db_get_value((db_addr_t)&fp->fr_pc, sizeof(fp->fr_pc), FALSE); if (trap) { pc = npc; trap = 0; } if (!INKERNEL((vm_offset_t)pc)) break; sym = db_search_symbol(pc, DB_STGY_ANY, &offset); if (sym == C_DB_SYM_NULL) { value = 0; name = NULL; } else db_symbol_values(sym, &name, &value); if (name == NULL) name = "(null)"; fp = (struct frame *)(db_get_value((db_addr_t)&fp->fr_fp, sizeof(fp->fr_fp), FALSE) + SPOFF); if (bcmp(name, "tl0_", 4) == 0 || bcmp(name, "tl1_", 4) == 0) { tf = (struct trapframe *)(fp + 1); npc = db_get_value((db_addr_t)&tf->tf_tpc, sizeof(tf->tf_tpc), FALSE); user = db_print_trap(td, tf); trap = 1; } else { db_printf("%s() at ", name); db_printsym(pc, DB_STGY_PROC); db_printf("\n"); } } return (0); } -void -db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, - char *modif) -{ - struct thread *td; - - td = (have_addr) ? kdb_thr_lookup(addr) : kdb_thread; - if (td == NULL) { - db_printf("Thread %d not found\n", (int)addr); - return; - } - db_trace_thread(td, count); -} - void db_trace_self(void) { db_expr_t addr; addr = (db_expr_t)__builtin_frame_address(1); db_backtrace(curthread, (struct frame *)(addr + SPOFF), -1); } int db_trace_thread(struct thread *td, int count) { struct pcb *ctx; ctx = kdb_thr_ctx(td); return (db_backtrace(td, (struct frame*)(ctx->pcb_sp + SPOFF), count)); }