db_trace.c revision 5
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/* 27 * HISTORY 28 * $Log: db_trace.c,v $ 29 * Revision 1.1 1992/03/25 21:42:05 pace 30 * Initial revision 31 * 32 * Revision 2.6 91/02/05 17:11:21 mrt 33 * Changed to new Mach copyright 34 * [91/02/01 17:31:32 mrt] 35 * 36 * Revision 2.5 91/01/09 19:55:27 rpd 37 * Fixed stack tracing for threads without kernel stacks. 38 * [91/01/09 rpd] 39 * 40 * Revision 2.4 91/01/08 15:10:22 rpd 41 * Reorganized the pcb. 42 * [90/12/11 rpd] 43 * 44 * Revision 2.3 90/11/05 14:27:07 rpd 45 * If we can not guess the number of args to a function, use 5 vs 0. 46 * [90/11/02 rvb] 47 * 48 * Revision 2.2 90/08/27 21:56:20 dbg 49 * Import db_sym.h. 50 * [90/08/21 dbg] 51 * Fix includes. 52 * [90/08/08 dbg] 53 * Created from rvb's code for new debugger. 54 * [90/07/11 dbg] 55 * 56 */ 57#include "param.h" 58#include "proc.h" 59#include <machine/db_machdep.h> 60 61#include <ddb/db_access.h> 62#include <ddb/db_sym.h> 63#include <ddb/db_variables.h> 64 65/* 66 * Machine register set. 67 */ 68struct db_variable db_regs[] = { 69 "cs", (int *)&ddb_regs.tf_cs, FCN_NULL, 70 "ds", (int *)&ddb_regs.tf_ds, FCN_NULL, 71 "es", (int *)&ddb_regs.tf_es, FCN_NULL, 72#if 0 73 "fs", (int *)&ddb_regs.tf_fs, FCN_NULL, 74 "gs", (int *)&ddb_regs.tf_gs, FCN_NULL, 75#endif 76 "ss", (int *)&ddb_regs.tf_ss, FCN_NULL, 77 "eax", (int *)&ddb_regs.tf_eax, FCN_NULL, 78 "ecx", (int *)&ddb_regs.tf_ecx, FCN_NULL, 79 "edx", (int *)&ddb_regs.tf_edx, FCN_NULL, 80 "ebx", (int *)&ddb_regs.tf_ebx, FCN_NULL, 81 "esp", (int *)&ddb_regs.tf_esp,FCN_NULL, 82 "ebp", (int *)&ddb_regs.tf_ebp, FCN_NULL, 83 "esi", (int *)&ddb_regs.tf_esi, FCN_NULL, 84 "edi", (int *)&ddb_regs.tf_edi, FCN_NULL, 85 "eip", (int *)&ddb_regs.tf_eip, FCN_NULL, 86 "efl", (int *)&ddb_regs.tf_eflags, FCN_NULL, 87}; 88struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); 89 90/* 91 * Stack trace. 92 */ 93#define INKERNEL(va) (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS) 94 95struct i386_frame { 96 struct i386_frame *f_frame; 97 int f_retaddr; 98 int f_arg0; 99}; 100 101#define TRAP 1 102#define INTERRUPT 2 103 104db_addr_t db_trap_symbol_value = 0; 105db_addr_t db_kdintr_symbol_value = 0; 106boolean_t db_trace_symbols_found = FALSE; 107 108void 109db_find_trace_symbols() 110{ 111 db_expr_t value; 112 if (db_value_of_name("_trap", &value)) 113 db_trap_symbol_value = (db_addr_t) value; 114 if (db_value_of_name("_kdintr", &value)) 115 db_kdintr_symbol_value = (db_addr_t) value; 116 db_trace_symbols_found = TRUE; 117} 118 119/* 120 * Figure out how many arguments were passed into the frame at "fp". 121 */ 122int 123db_numargs(fp) 124 struct i386_frame *fp; 125{ 126 int *argp; 127 int inst; 128 int args; 129 extern char etext[]; 130 131 argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE); 132 if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext) 133 args = 5; 134 else { 135 inst = db_get_value((int)argp, 4, FALSE); 136 if ((inst & 0xff) == 0x59) /* popl %ecx */ 137 args = 1; 138 else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */ 139 args = ((inst >> 16) & 0xff) / 4; 140 else 141 args = 5; 142 } 143 return (args); 144} 145 146/* 147 * Figure out the next frame up in the call stack. 148 * For trap(), we print the address of the faulting instruction and 149 * proceed with the calling frame. We return the ip that faulted. 150 * If the trap was caused by jumping through a bogus pointer, then 151 * the next line in the backtrace will list some random function as 152 * being called. It should get the argument list correct, though. 153 * It might be possible to dig out from the next frame up the name 154 * of the function that faulted, but that could get hairy. 155 */ 156void 157db_nextframe(fp, ip, argp, is_trap) 158 struct i386_frame **fp; /* in/out */ 159 db_addr_t *ip; /* out */ 160 int *argp; /* in */ 161 int is_trap; /* in */ 162{ 163 struct i386_saved_state *saved_regs; 164 165 if (is_trap == 0) { 166 *ip = (db_addr_t) 167 db_get_value((int) &(*fp)->f_retaddr, 4, FALSE); 168 *fp = (struct i386_frame *) 169 db_get_value((int) &(*fp)->f_frame, 4, FALSE); 170 } else { 171 /* 172 * We know that trap() has 1 argument and we know that 173 * it is an (int *). 174 */ 175 saved_regs = (struct i386_saved_state *) 176 db_get_value((int)argp, 4, FALSE); 177 db_printf("--- trap (number %d) ---\n", 178 saved_regs->tf_trapno & 0xffff); 179 db_printsym(saved_regs->tf_eip, DB_STGY_XTRN); 180 db_printf(":\n"); 181 *fp = (struct i386_frame *)saved_regs->tf_ebp; 182 *ip = (db_addr_t)saved_regs->tf_eip; 183 } 184 185} 186 187void 188db_stack_trace_cmd(addr, have_addr, count, modif) 189 db_expr_t addr; 190 boolean_t have_addr; 191 db_expr_t count; 192 char *modif; 193{ 194 struct i386_frame *frame, *lastframe; 195 int *argp; 196 db_addr_t callpc; 197 int is_trap; 198 boolean_t kernel_only = TRUE; 199 boolean_t trace_thread = FALSE; 200 201 if (!db_trace_symbols_found) 202 db_find_trace_symbols(); 203 204 { 205 register char *cp = modif; 206 register char c; 207 208 while ((c = *cp++) != 0) { 209 if (c == 't') 210 trace_thread = TRUE; 211 if (c == 'u') 212 kernel_only = FALSE; 213 } 214 } 215 216 if (count == -1) 217 count = 65535; 218 219 if (!have_addr) { 220 frame = (struct i386_frame *)ddb_regs.tf_ebp; 221 callpc = (db_addr_t)ddb_regs.tf_eip; 222 } 223 else if (trace_thread) { 224 printf ("db_trace.c: can't trace thread\n"); 225 } 226 else { 227 frame = (struct i386_frame *)addr; 228 callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE); 229 } 230 231 lastframe = 0; 232 while (count-- && frame != 0) { 233 register int narg; 234 char * name; 235 db_expr_t offset; 236 237 if (INKERNEL((int)frame) && callpc == db_trap_symbol_value) { 238 narg = 1; 239 is_trap = TRAP; 240 } 241 else 242 if (INKERNEL((int)frame) && callpc == db_kdintr_symbol_value) { 243 is_trap = INTERRUPT; 244 narg = 0; 245 } 246 else { 247 is_trap = 0; 248 narg = db_numargs(frame); 249 } 250 251 db_find_sym_and_offset(callpc, &name, &offset); 252 db_printf("%s(", name); 253 254 argp = &frame->f_arg0; 255 while (narg) { 256 db_printf("%x", db_get_value((int)argp, 4, FALSE)); 257 argp++; 258 if (--narg != 0) 259 db_printf(","); 260 } 261 db_printf(") at "); 262 db_printsym(callpc, DB_STGY_XTRN); 263 db_printf("\n"); 264 265 lastframe = frame; 266 db_nextframe(&frame, &callpc, &frame->f_arg0, is_trap); 267 268 if (frame == 0) { 269 /* end of chain */ 270 break; 271 } 272 if (INKERNEL((int)frame)) { 273 /* staying in kernel */ 274 if (frame <= lastframe) { 275 db_printf("Bad frame pointer: 0x%x\n", frame); 276 break; 277 } 278 } 279 else if (INKERNEL((int)lastframe)) { 280 /* switch from user to kernel */ 281 if (kernel_only) 282 break; /* kernel stack only */ 283 } 284 else { 285 /* in user */ 286 if (frame <= lastframe) { 287 db_printf("Bad frame pointer: 0x%x\n", frame); 288 break; 289 } 290 } 291 } 292} 293