db_trace.c revision 27535
1/* 2 * Mach Operating System 3 * Copyright (c) 1991,1990 Carnegie Mellon University 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify and distribute this software and its 7 * documentation is hereby granted, provided that both the copyright 8 * notice and this permission notice appear in all copies of the 9 * software, derivative works or modified versions, and any portions 10 * thereof, and that both notices appear in supporting documentation. 11 * 12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 15 * 16 * Carnegie Mellon requests users of this software to return to 17 * 18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 19 * School of Computer Science 20 * Carnegie Mellon University 21 * Pittsburgh PA 15213-3890 22 * 23 * any improvements or extensions that they make and grant Carnegie the 24 * rights to redistribute these changes. 25 * 26 * $Id: db_trace.c,v 1.22 1997/04/26 19:12:03 peter Exp $ 27 */ 28 29#include <sys/param.h> 30#include <sys/systm.h> 31 32#include <machine/md_var.h> 33#include <machine/segments.h> 34 35#include <vm/vm.h> 36#include <vm/vm_param.h> 37#include <vm/pmap.h> 38#include <ddb/ddb.h> 39 40#include <ddb/db_access.h> 41#include <ddb/db_sym.h> 42#include <ddb/db_variables.h> 43 44/* 45 * Machine register set. 46 */ 47struct db_variable db_regs[] = { 48 "cs", (int *)&ddb_regs.tf_cs, FCN_NULL, 49 "ds", (int *)&ddb_regs.tf_ds, FCN_NULL, 50 "es", (int *)&ddb_regs.tf_es, FCN_NULL, 51#if 0 52 "fs", (int *)&ddb_regs.tf_fs, FCN_NULL, 53 "gs", (int *)&ddb_regs.tf_gs, FCN_NULL, 54#endif 55 "ss", (int *)&ddb_regs.tf_ss, FCN_NULL, 56 "eax", (int *)&ddb_regs.tf_eax, FCN_NULL, 57 "ecx", (int *)&ddb_regs.tf_ecx, FCN_NULL, 58 "edx", (int *)&ddb_regs.tf_edx, FCN_NULL, 59 "ebx", (int *)&ddb_regs.tf_ebx, FCN_NULL, 60 "esp", (int *)&ddb_regs.tf_esp,FCN_NULL, 61 "ebp", (int *)&ddb_regs.tf_ebp, FCN_NULL, 62 "esi", (int *)&ddb_regs.tf_esi, FCN_NULL, 63 "edi", (int *)&ddb_regs.tf_edi, FCN_NULL, 64 "eip", (int *)&ddb_regs.tf_eip, FCN_NULL, 65 "efl", (int *)&ddb_regs.tf_eflags, FCN_NULL, 66}; 67struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); 68 69/* 70 * Stack trace. 71 */ 72#define INKERNEL(va) (((vm_offset_t)(va)) >= USRSTACK) 73 74struct i386_frame { 75 struct i386_frame *f_frame; 76 int f_retaddr; 77 int f_arg0; 78}; 79 80#define NORMAL 0 81#define TRAP 1 82#define INTERRUPT 2 83#define SYSCALL 3 84 85static void db_nextframe __P((struct i386_frame **, db_addr_t *)); 86static int db_numargs __P((struct i386_frame *)); 87static void db_print_stack_entry __P((char *, int, char **, int *, db_addr_t)); 88 89/* 90 * Figure out how many arguments were passed into the frame at "fp". 91 */ 92static int 93db_numargs(fp) 94 struct i386_frame *fp; 95{ 96 int *argp; 97 int inst; 98 int args; 99 100 argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE); 101 if (argp < (int *)VM_MIN_KERNEL_ADDRESS || 102 argp > (int *)etext) { 103 args = 5; 104 } else { 105 inst = db_get_value((int)argp, 4, FALSE); 106 if ((inst & 0xff) == 0x59) /* popl %ecx */ 107 args = 1; 108 else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */ 109 args = ((inst >> 16) & 0xff) / 4; 110 else 111 args = 5; 112 } 113 return (args); 114} 115 116static void 117db_print_stack_entry(name, narg, argnp, argp, callpc) 118 char *name; 119 int narg; 120 char **argnp; 121 int *argp; 122 db_addr_t callpc; 123{ 124 db_printf("%s(", name); 125 while (narg) { 126 if (argnp) 127 db_printf("%s=", *argnp++); 128 db_printf("%n", db_get_value((int)argp, 4, FALSE)); 129 argp++; 130 if (--narg != 0) 131 db_printf(","); 132 } 133 db_printf(") at "); 134 db_printsym(callpc, DB_STGY_PROC); 135 db_printf("\n"); 136} 137 138/* 139 * Figure out the next frame up in the call stack. 140 */ 141static void 142db_nextframe(fp, ip) 143 struct i386_frame **fp; /* in/out */ 144 db_addr_t *ip; /* out */ 145{ 146 struct trapframe *tf; 147 int frame_type; 148 int eip, esp, ebp; 149 db_expr_t offset; 150 char *sym, *name; 151 152 eip = db_get_value((int) &(*fp)->f_retaddr, 4, FALSE); 153 ebp = db_get_value((int) &(*fp)->f_frame, 4, FALSE); 154 155 /* 156 * Figure out frame type. 157 */ 158 159 frame_type = NORMAL; 160 161 sym = db_search_symbol(eip, DB_STGY_ANY, &offset); 162 db_symbol_values(sym, &name, NULL); 163 if (name != NULL) { 164 if (!strcmp(name, "calltrap")) { 165 frame_type = TRAP; 166 } else if (!strncmp(name, "Xresume", 7)) { 167 frame_type = INTERRUPT; 168 } else if (!strcmp(name, "_Xsyscall")) { 169 frame_type = SYSCALL; 170 } 171 } 172 173 /* 174 * Normal frames need no special processing. 175 */ 176 if (frame_type == NORMAL) { 177 *ip = (db_addr_t) eip; 178 *fp = (struct i386_frame *) ebp; 179 return; 180 } 181 182 db_print_stack_entry(name, 0, 0, 0, eip); 183 184 /* 185 * Point to base of trapframe which is just above the 186 * current frame. 187 */ 188 tf = (struct trapframe *) ((int)*fp + 8); 189 190 esp = (ISPL(tf->tf_cs) == SEL_UPL) ? tf->tf_esp : (int)&tf->tf_esp; 191 switch (frame_type) { 192 case TRAP: 193 if (INKERNEL((int) tf)) { 194 eip = tf->tf_eip; 195 ebp = tf->tf_ebp; 196 db_printf( 197 "--- trap %#n, eip = %#n, esp = %#n, ebp = %#n ---\n", 198 tf->tf_trapno, eip, esp, ebp); 199 } 200 break; 201 case SYSCALL: 202 if (INKERNEL((int) tf)) { 203 eip = tf->tf_eip; 204 ebp = tf->tf_ebp; 205 db_printf( 206 "--- syscall %#n, eip = %#n, esp = %#n, ebp = %#n ---\n", 207 tf->tf_eax, eip, esp, ebp); 208 } 209 break; 210 case INTERRUPT: 211 tf = (struct trapframe *)((int)*fp + 16); 212 if (INKERNEL((int) tf)) { 213 eip = tf->tf_eip; 214 ebp = tf->tf_ebp; 215 db_printf( 216 "--- interrupt, eip = %#n, esp = %#n, ebp = %#n ---\n", 217 eip, esp, ebp); 218 } 219 break; 220 default: 221 break; 222 } 223 224 *ip = (db_addr_t) eip; 225 *fp = (struct i386_frame *) ebp; 226} 227 228void 229db_stack_trace_cmd(addr, have_addr, count, modif) 230 db_expr_t addr; 231 boolean_t have_addr; 232 db_expr_t count; 233 char *modif; 234{ 235 struct i386_frame *frame; 236 int *argp; 237 db_addr_t callpc; 238 boolean_t first; 239 240 if (count == -1) 241 count = 65535; 242 243 if (!have_addr) { 244 frame = (struct i386_frame *)ddb_regs.tf_ebp; 245 if (frame == NULL) 246 frame = (struct i386_frame *)(ddb_regs.tf_esp - 4); 247 callpc = (db_addr_t)ddb_regs.tf_eip; 248 } else { 249 frame = (struct i386_frame *)addr; 250 callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE); 251 } 252 253 first = TRUE; 254 while (count--) { 255 struct i386_frame *actframe; 256 int narg; 257 char * name; 258 db_expr_t offset; 259 db_sym_t sym; 260#define MAXNARG 16 261 char *argnames[MAXNARG], **argnp = NULL; 262 263 sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); 264 db_symbol_values(sym, &name, NULL); 265 266 /* 267 * Attempt to determine a (possibly fake) frame that gives 268 * the caller's pc. It may differ from `frame' if the 269 * current function never sets up a standard frame or hasn't 270 * set one up yet or has just discarded one. The last two 271 * cases can be guessed fairly reliably for code generated 272 * by gcc. The first case is too much trouble to handle in 273 * general because the amount of junk on the stack depends 274 * on the pc (the special handling of "calltrap", etc. in 275 * db_nextframe() works because the `next' pc is special). 276 */ 277 actframe = frame; 278 if (first && !have_addr) { 279 int instr; 280 281 instr = db_get_value(callpc, 4, FALSE); 282 if ((instr & 0x00ffffff) == 0x00e58955) { 283 /* pushl %ebp; movl %esp, %ebp */ 284 actframe = (struct i386_frame *) 285 (ddb_regs.tf_esp - 4); 286 } else if ((instr & 0x0000ffff) == 0x0000e589) { 287 /* movl %esp, %ebp */ 288 actframe = (struct i386_frame *) 289 ddb_regs.tf_esp; 290 if (ddb_regs.tf_ebp == 0) { 291 /* Fake the caller's frame better. */ 292 frame = actframe; 293 } 294 } else if ((instr & 0x000000ff) == 0x000000c3) { 295 /* ret */ 296 actframe = (struct i386_frame *) 297 (ddb_regs.tf_esp - 4); 298 } else if (offset == 0) { 299 /* Probably a symbol in assembler code. */ 300 actframe = (struct i386_frame *) 301 (ddb_regs.tf_esp - 4); 302 } 303 } 304 first = FALSE; 305 306 argp = &actframe->f_arg0; 307 narg = MAXNARG; 308 if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) { 309 argnp = argnames; 310 } else { 311 narg = db_numargs(frame); 312 } 313 314 db_print_stack_entry(name, narg, argnp, argp, callpc); 315 316 if (actframe != frame) { 317 /* `frame' belongs to caller. */ 318 callpc = (db_addr_t) 319 db_get_value((int)&actframe->f_retaddr, 4, FALSE); 320 continue; 321 } 322 323 db_nextframe(&frame, &callpc); 324 325 if (INKERNEL((int) callpc) && !INKERNEL((int) frame)) { 326 sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); 327 db_symbol_values(sym, &name, NULL); 328 db_print_stack_entry(name, 0, 0, 0, callpc); 329 break; 330 } 331 if (!INKERNEL((int) frame)) { 332 break; 333 } 334 } 335} 336