/* $NetBSD: db_trace.c,v 1.3 2011/04/21 00:24:07 enami Exp $ */ /* * 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 __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.3 2011/04/21 00:24:07 enami Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int db_x86_regop(const struct db_variable *vp, db_expr_t *val, int opcode) { db_expr_t *regaddr = (db_expr_t *)(((uint8_t *)DDB_REGS) + ((size_t)vp->valuep)); switch (opcode) { case DB_VAR_GET: *val = *regaddr; break; case DB_VAR_SET: *regaddr = *val; break; default: db_printf("db_x86_regop: unknown op %d", opcode); db_error(NULL); } return 0; } /* * Stack trace. */ #if 0 db_addr_t db_trap_symbol_value = 0; db_addr_t db_syscall_symbol_value = 0; db_addr_t db_kdintr_symbol_value = 0; bool db_trace_symbols_found = false; void db_find_trace_symbols(void); void db_find_trace_symbols(void) { db_expr_t value; if (db_value_of_name("_trap", &value)) db_trap_symbol_value = (db_addr_t) value; if (db_value_of_name("_kdintr", &value)) db_kdintr_symbol_value = (db_addr_t) value; if (db_value_of_name("_syscall", &value)) db_syscall_symbol_value = (db_addr_t) value; db_trace_symbols_found = true; } #endif void db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif, void (*pr)(const char *, ...)) { long *frame, *lastframe; long *retaddr, *arg0; long *argp; db_addr_t callpc; int is_trap; bool kernel_only = true; bool trace_thread = false; bool lwpaddr = false; #if 0 if (!db_trace_symbols_found) db_find_trace_symbols(); #endif { const char *cp = modif; char c; while ((c = *cp++) != 0) { if (c == 'a') { lwpaddr = true; trace_thread = true; } if (c == 't') trace_thread = true; if (c == 'u') kernel_only = false; } } #define set_frame_callpc() do { \ frame = (long *)ddb_regs.tf_bp; \ callpc = (db_addr_t)ddb_regs.tf_ip; \ } while (/*CONSTCCOND*/0) if (have_addr && trace_thread) { struct pcb *pcb; proc_t p; lwp_t l; if (lwpaddr) { db_read_bytes(addr, sizeof(l), (char *)&l); db_read_bytes((db_addr_t)l.l_proc, sizeof(p), (char *)&p); (*pr)("trace: pid %d ", p.p_pid); } else { proc_t *pp; (*pr)("trace: pid %d ", (int)addr); if ((pp = db_proc_find((pid_t)addr)) == 0) { (*pr)("not found\n"); return; } db_read_bytes((db_addr_t)pp, sizeof(p), (char *)&p); addr = (db_addr_t)p.p_lwps.lh_first; db_read_bytes(addr, sizeof(l), (char *)&l); } (*pr)("lid %d ", l.l_lid); pcb = lwp_getpcb(&l); #ifdef _KERNEL if (l.l_proc == curproc && (lwp_t *)addr == curlwp) set_frame_callpc(); else #endif { db_read_bytes((db_addr_t)&pcb->pcb_bp, sizeof(frame), (char *)&frame); db_read_bytes((db_addr_t)(frame + 1), sizeof(callpc), (char *)&callpc); db_read_bytes((db_addr_t)frame, sizeof(frame), (char *)&frame); } (*pr)("at %p\n", frame); } else if (have_addr) { frame = (long *)addr; db_read_bytes((db_addr_t)(frame + 1), sizeof(callpc), (char *)&callpc); db_read_bytes((db_addr_t)frame, sizeof(frame), (char *)&frame); } else set_frame_callpc(); retaddr = frame + 1; arg0 = frame + 2; lastframe = 0; while (count && frame != 0) { int narg; const char * name; db_expr_t offset; db_sym_t sym; char *argnames[MAXNARG], **argnp = NULL; db_addr_t lastcallpc; name = "?"; is_trap = NONE; offset = 0; sym = db_frame_info(frame, callpc, &name, &offset, &is_trap, &narg); if (lastframe == 0 && sym == (db_sym_t)0 && callpc != 0) { /* Symbol not found, peek at code */ u_long instr = db_get_value(callpc, 4, false); offset = 1; if ( #ifdef __x86_64__ instr == 0xe5894855 || /* enter: pushq %rbp, movq %rsp, %rbp */ (instr & 0x00ffffff) == 0x0048e589 /* enter+1: movq %rsp, %rbp */) #else (instr & 0x00ffffff) == 0x00e58955 || /* enter: pushl %ebp, movl %esp, %ebp */ (instr & 0x0000ffff) == 0x0000e589 /* enter+1: movl %esp, %ebp */) #endif { offset = 0; } } if (is_trap == NONE) { if (db_sym_numargs(sym, &narg, argnames)) argnp = argnames; else narg = db_numargs(frame); } (*pr)("%s(", name); if (lastframe == 0 && offset == 0 && !have_addr) { /* * We have a breakpoint before the frame is set up * Use %[er]sp instead */ argp = (long *)&((struct x86_frame *) (ddb_regs.tf_sp-sizeof(long)))->f_arg0; } else { argp = frame + 2; } while (narg) { if (argnp) (*pr)("%s=", *argnp++); (*pr)("%lx", db_get_value((long)argp, sizeof(long), false)); argp++; if (--narg != 0) (*pr)(","); } (*pr)(") at "); db_printsym(callpc, DB_STGY_PROC, pr); (*pr)("\n"); if (lastframe == 0 && offset == 0 && !have_addr) { /* Frame really belongs to next callpc */ struct x86_frame *fp = (void *) (ddb_regs.tf_sp-sizeof(long)); lastframe = (long *)fp; callpc = (db_addr_t) db_get_value((db_addr_t)&fp->f_retaddr, sizeof(long), false); continue; } lastframe = frame; lastcallpc = callpc; if (!db_nextframe(&frame, &retaddr, &arg0, &callpc, frame + 2, is_trap, pr)) break; if (INKERNEL((long)frame)) { /* staying in kernel */ #ifdef __i386__ if (!db_intrstack_p(frame) && db_intrstack_p(lastframe)) { (*pr)("--- switch to interrupt stack ---\n"); } else #endif if (frame < lastframe || (frame == lastframe && callpc == lastcallpc)) { (*pr)("Bad frame pointer: %p\n", frame); break; } } else if (INKERNEL((long)lastframe)) { /* switch from user to kernel */ if (kernel_only) break; /* kernel stack only */ } else { /* in user */ if (frame <= lastframe) { (*pr)("Bad user frame pointer: %p\n", frame); break; } } --count; } if (count && is_trap != NONE) { db_printsym(callpc, DB_STGY_XTRN, pr); (*pr)(":\n"); } #undef set_frame_callpc }