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