Annotation of sys/arch/i386/i386/db_trace.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: db_trace.c,v 1.13 2006/11/28 18:56:17 uwe Exp $ */
! 2: /* $NetBSD: db_trace.c,v 1.18 1996/05/03 19:42:01 christos Exp $ */
! 3:
! 4: /*
! 5: * Mach Operating System
! 6: * Copyright (c) 1991,1990 Carnegie Mellon University
! 7: * All Rights Reserved.
! 8: *
! 9: * Permission to use, copy, modify and distribute this software and its
! 10: * documentation is hereby granted, provided that both the copyright
! 11: * notice and this permission notice appear in all copies of the
! 12: * software, derivative works or modified versions, and any portions
! 13: * thereof, and that both notices appear in supporting documentation.
! 14: *
! 15: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
! 16: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
! 17: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
! 18: *
! 19: * Carnegie Mellon requests users of this software to return to
! 20: *
! 21: * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
! 22: * School of Computer Science
! 23: * Carnegie Mellon University
! 24: * Pittsburgh PA 15213-3890
! 25: *
! 26: * any improvements or extensions that they make and grant Carnegie Mellon
! 27: * the rights to redistribute these changes.
! 28: */
! 29:
! 30: #include <sys/param.h>
! 31: #include <sys/systm.h>
! 32: #include <sys/proc.h>
! 33: #include <sys/user.h>
! 34:
! 35: #include <machine/db_machdep.h>
! 36:
! 37: #include <ddb/db_sym.h>
! 38: #include <ddb/db_access.h>
! 39: #include <ddb/db_variables.h>
! 40: #include <ddb/db_output.h>
! 41: #include <ddb/db_interface.h>
! 42:
! 43: /*
! 44: * Machine register set.
! 45: */
! 46: struct db_variable db_regs[] = {
! 47: { "ds", (long *)&ddb_regs.tf_ds, FCN_NULL },
! 48: { "es", (long *)&ddb_regs.tf_es, FCN_NULL },
! 49: { "fs", (long *)&ddb_regs.tf_fs, FCN_NULL },
! 50: { "gs", (long *)&ddb_regs.tf_gs, FCN_NULL },
! 51: { "edi", (long *)&ddb_regs.tf_edi, FCN_NULL },
! 52: { "esi", (long *)&ddb_regs.tf_esi, FCN_NULL },
! 53: { "ebp", (long *)&ddb_regs.tf_ebp, FCN_NULL },
! 54: { "ebx", (long *)&ddb_regs.tf_ebx, FCN_NULL },
! 55: { "edx", (long *)&ddb_regs.tf_edx, FCN_NULL },
! 56: { "ecx", (long *)&ddb_regs.tf_ecx, FCN_NULL },
! 57: { "eax", (long *)&ddb_regs.tf_eax, FCN_NULL },
! 58: { "eip", (long *)&ddb_regs.tf_eip, FCN_NULL },
! 59: { "cs", (long *)&ddb_regs.tf_cs, FCN_NULL },
! 60: { "eflags", (long *)&ddb_regs.tf_eflags, FCN_NULL },
! 61: { "esp", (long *)&ddb_regs.tf_esp, FCN_NULL },
! 62: { "ss", (long *)&ddb_regs.tf_ss, FCN_NULL },
! 63: };
! 64: struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
! 65:
! 66: /*
! 67: * Stack trace.
! 68: */
! 69: #define INKERNEL(va) (((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
! 70:
! 71: struct i386_frame {
! 72: struct i386_frame *f_frame;
! 73: int f_retaddr;
! 74: int f_arg0;
! 75: };
! 76:
! 77: #define NONE 0
! 78: #define TRAP 1
! 79: #define SYSCALL 2
! 80: #define INTERRUPT 3
! 81:
! 82: db_addr_t db_trap_symbol_value = 0;
! 83: db_addr_t db_syscall_symbol_value = 0;
! 84: db_addr_t db_kdintr_symbol_value = 0;
! 85: boolean_t db_trace_symbols_found = FALSE;
! 86:
! 87: void db_find_trace_symbols(void);
! 88: int db_numargs(struct i386_frame *);
! 89: void db_nextframe(struct i386_frame **, db_addr_t *, int *, int,
! 90: int (*pr)(const char *, ...));
! 91:
! 92: void
! 93: db_find_trace_symbols(void)
! 94: {
! 95: db_expr_t value;
! 96:
! 97: if (db_value_of_name("trap", &value))
! 98: db_trap_symbol_value = (db_addr_t) value;
! 99: if (db_value_of_name("kdintr", &value))
! 100: db_kdintr_symbol_value = (db_addr_t) value;
! 101: if (db_value_of_name("syscall", &value))
! 102: db_syscall_symbol_value = (db_addr_t) value;
! 103: db_trace_symbols_found = TRUE;
! 104: }
! 105:
! 106: /*
! 107: * Figure out how many arguments were passed into the frame at "fp".
! 108: */
! 109: int
! 110: db_numargs(struct i386_frame *fp)
! 111: {
! 112: int *argp;
! 113: int inst;
! 114: int args;
! 115: extern char etext[];
! 116:
! 117: argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE);
! 118: if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext) {
! 119: args = 5;
! 120: } else {
! 121: inst = db_get_value((int)argp, 4, FALSE);
! 122: if ((inst & 0xff) == 0x59) /* popl %ecx */
! 123: args = 1;
! 124: else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */
! 125: args = ((inst >> 16) & 0xff) / 4;
! 126: else
! 127: args = 5;
! 128: }
! 129: return (args);
! 130: }
! 131:
! 132: /*
! 133: * Figure out the next frame up in the call stack.
! 134: * For trap(), we print the address of the faulting instruction and
! 135: * proceed with the calling frame. We return the ip that faulted.
! 136: * If the trap was caused by jumping through a bogus pointer, then
! 137: * the next line in the backtrace will list some random function as
! 138: * being called. It should get the argument list correct, though.
! 139: * It might be possible to dig out from the next frame up the name
! 140: * of the function that faulted, but that could get hairy.
! 141: */
! 142: void
! 143: db_nextframe(struct i386_frame **fp, db_addr_t *ip, int *argp, int is_trap,
! 144: int (*pr)(const char *, ...))
! 145: {
! 146:
! 147: switch (is_trap) {
! 148: case NONE:
! 149: *ip = (db_addr_t)
! 150: db_get_value((int) &(*fp)->f_retaddr, 4, FALSE);
! 151: *fp = (struct i386_frame *)
! 152: db_get_value((int) &(*fp)->f_frame, 4, FALSE);
! 153: break;
! 154:
! 155: default: {
! 156: struct trapframe *tf;
! 157:
! 158: /* The only argument to trap() or syscall() is the trapframe. */
! 159: tf = (struct trapframe *)argp;
! 160: switch (is_trap) {
! 161: case TRAP:
! 162: (*pr)("--- trap (number %d) ---\n", tf->tf_trapno);
! 163: break;
! 164: case SYSCALL:
! 165: (*pr)("--- syscall (number %d) ---\n", tf->tf_eax);
! 166: break;
! 167: case INTERRUPT:
! 168: (*pr)("--- interrupt ---\n");
! 169: break;
! 170: }
! 171: *fp = (struct i386_frame *)tf->tf_ebp;
! 172: *ip = (db_addr_t)tf->tf_eip;
! 173: break;
! 174: }
! 175: }
! 176: }
! 177:
! 178: void
! 179: db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count,
! 180: char *modif, int (*pr)(const char *, ...))
! 181: {
! 182: struct i386_frame *frame, *lastframe;
! 183: int *argp;
! 184: db_addr_t callpc;
! 185: int is_trap = 0;
! 186: boolean_t kernel_only = TRUE;
! 187: boolean_t trace_thread = FALSE;
! 188: boolean_t trace_proc = FALSE;
! 189:
! 190: #if 0
! 191: if (!db_trace_symbols_found)
! 192: db_find_trace_symbols();
! 193: #endif
! 194:
! 195: {
! 196: char *cp = modif;
! 197: char c;
! 198:
! 199: while ((c = *cp++) != 0) {
! 200: if (c == 't')
! 201: trace_thread = TRUE;
! 202: if (c == 'p')
! 203: trace_proc = TRUE;
! 204: if (c == 'u')
! 205: kernel_only = FALSE;
! 206: }
! 207: }
! 208:
! 209: if (count == -1)
! 210: count = 65535;
! 211:
! 212: if (!have_addr) {
! 213: frame = (struct i386_frame *)ddb_regs.tf_ebp;
! 214: callpc = (db_addr_t)ddb_regs.tf_eip;
! 215: } else if (trace_thread) {
! 216: (*pr) ("db_trace.c: can't trace thread\n");
! 217: } else if (trace_proc) {
! 218: struct proc *p = pfind((pid_t)addr);
! 219: if (p == NULL) {
! 220: (*pr) ("db_trace.c: process not found\n");
! 221: return;
! 222: }
! 223: frame = (struct i386_frame *)p->p_addr->u_pcb.pcb_ebp;
! 224: callpc = (db_addr_t)
! 225: db_get_value((int)&frame->f_retaddr, 4, FALSE);
! 226: } else {
! 227: frame = (struct i386_frame *)addr;
! 228: callpc = (db_addr_t)
! 229: db_get_value((int)&frame->f_retaddr, 4, FALSE);
! 230: }
! 231:
! 232: lastframe = 0;
! 233: while (count && frame != 0) {
! 234: int narg;
! 235: char * name;
! 236: db_expr_t offset;
! 237: db_sym_t sym;
! 238: #define MAXNARG 16
! 239: char *argnames[MAXNARG], **argnp = NULL;
! 240:
! 241: sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
! 242: db_symbol_values(sym, &name, NULL);
! 243:
! 244: if (lastframe == 0 && sym == NULL) {
! 245: /* Symbol not found, peek at code */
! 246: int instr = db_get_value(callpc, 4, FALSE);
! 247:
! 248: offset = 1;
! 249: if ((instr & 0x00ffffff) == 0x00e58955 ||
! 250: /* enter: pushl %ebp, movl %esp, %ebp */
! 251: (instr & 0x0000ffff) == 0x0000e589
! 252: /* enter+1: movl %esp, %ebp */) {
! 253: offset = 0;
! 254: }
! 255: }
! 256: if (INKERNEL((int)frame) && name) {
! 257: if (!strcmp(name, "trap")) {
! 258: is_trap = TRAP;
! 259: } else if (!strcmp(name, "syscall")) {
! 260: is_trap = SYSCALL;
! 261: } else if (!strncmp(name, "Xintr", 5) ||
! 262: !strncmp(name, "Xresume", 7) ||
! 263: !strncmp(name, "Xstray", 6) ||
! 264: !strncmp(name, "Xhold", 5) ||
! 265: !strncmp(name, "Xrecurse", 8) ||
! 266: !strcmp(name, "Xdoreti") ||
! 267: !strncmp(name, "Xsoft", 5)) {
! 268: is_trap = INTERRUPT;
! 269: } else
! 270: goto normal;
! 271: narg = 0;
! 272: } else {
! 273: normal:
! 274: is_trap = NONE;
! 275: narg = MAXNARG;
! 276: if (db_sym_numargs(sym, &narg, argnames))
! 277: argnp = argnames;
! 278: else
! 279: narg = db_numargs(frame);
! 280: }
! 281:
! 282: (*pr)("%s(", name);
! 283:
! 284: if (lastframe == 0 && offset == 0 && !have_addr) {
! 285: /*
! 286: * We have a breakpoint before the frame is set up
! 287: * Use %esp instead
! 288: */
! 289: argp = &((struct i386_frame *)(ddb_regs.tf_esp-4))->f_arg0;
! 290: } else {
! 291: argp = &frame->f_arg0;
! 292: }
! 293:
! 294: while (narg) {
! 295: if (argnp)
! 296: (*pr)("%s=", *argnp++);
! 297: (*pr)("%x", db_get_value((int)argp, 4, FALSE));
! 298: argp++;
! 299: if (--narg != 0)
! 300: (*pr)(",");
! 301: }
! 302: (*pr)(") at ");
! 303: db_printsym(callpc, DB_STGY_PROC, pr);
! 304: (*pr)("\n");
! 305:
! 306: if (lastframe == 0 && offset == 0 && !have_addr) {
! 307: /* Frame really belongs to next callpc */
! 308: lastframe = (struct i386_frame *)(ddb_regs.tf_esp-4);
! 309: callpc = (db_addr_t)
! 310: db_get_value((int)&lastframe->f_retaddr, 4, FALSE);
! 311: continue;
! 312: }
! 313:
! 314: lastframe = frame;
! 315: db_nextframe(&frame, &callpc, &frame->f_arg0, is_trap, pr);
! 316:
! 317: if (frame == 0) {
! 318: /* end of chain */
! 319: break;
! 320: }
! 321: if (INKERNEL((int)frame)) {
! 322: /* staying in kernel */
! 323: if (frame <= lastframe) {
! 324: (*pr)("Bad frame pointer: %p\n", frame);
! 325: break;
! 326: }
! 327: } else if (INKERNEL((int)lastframe)) {
! 328: /* switch from user to kernel */
! 329: if (kernel_only)
! 330: break; /* kernel stack only */
! 331: } else {
! 332: /* in user */
! 333: if (frame <= lastframe) {
! 334: (*pr)("Bad user frame pointer: %p\n",
! 335: frame);
! 336: break;
! 337: }
! 338: }
! 339: --count;
! 340: }
! 341:
! 342: if (count && is_trap != NONE) {
! 343: db_printsym(callpc, DB_STGY_XTRN, pr);
! 344: (*pr)(":\n");
! 345: }
! 346: }
CVSweb